/src/suricata7/src/detect.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2022 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | /** |
19 | | * \file |
20 | | * |
21 | | * \author Victor Julien <victor@inliniac.net> |
22 | | * |
23 | | * Basic detection engine |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "suricata.h" |
28 | | #include "conf.h" |
29 | | |
30 | | #include "decode.h" |
31 | | #include "packet.h" |
32 | | #include "flow.h" |
33 | | #include "stream-tcp.h" |
34 | | #include "app-layer.h" |
35 | | #include "app-layer-parser.h" |
36 | | #include "app-layer-frames.h" |
37 | | |
38 | | #include "detect.h" |
39 | | #include "detect-dsize.h" |
40 | | #include "detect-engine.h" |
41 | | #include "detect-engine-build.h" |
42 | | #include "detect-engine-frame.h" |
43 | | #include "detect-engine-profile.h" |
44 | | |
45 | | #include "detect-engine-alert.h" |
46 | | #include "detect-engine-siggroup.h" |
47 | | #include "detect-engine-address.h" |
48 | | #include "detect-engine-proto.h" |
49 | | #include "detect-engine-port.h" |
50 | | #include "detect-engine-mpm.h" |
51 | | #include "detect-engine-iponly.h" |
52 | | #include "detect-engine-threshold.h" |
53 | | #include "detect-engine-prefilter.h" |
54 | | #include "detect-engine-state.h" |
55 | | #include "detect-engine-analyzer.h" |
56 | | |
57 | | #include "detect-engine-payload.h" |
58 | | #include "detect-engine-event.h" |
59 | | |
60 | | #include "detect-filestore.h" |
61 | | #include "detect-flowvar.h" |
62 | | #include "detect-replace.h" |
63 | | |
64 | | #include "util-validate.h" |
65 | | #include "util-detect.h" |
66 | | #include "util-profiling.h" |
67 | | |
68 | | #include "action-globals.h" |
69 | | |
70 | | typedef struct DetectRunScratchpad { |
71 | | const AppProto alproto; |
72 | | const uint8_t flow_flags; /* flow/state flags: STREAM_* */ |
73 | | const bool app_decoder_events; |
74 | | const SigGroupHead *sgh; |
75 | | SignatureMask pkt_mask; |
76 | | } DetectRunScratchpad; |
77 | | |
78 | | /* prototypes */ |
79 | | static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx, |
80 | | DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow); |
81 | | static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx, |
82 | | DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p); |
83 | | static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx, |
84 | | Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch); |
85 | | static inline void DetectRunPrefilterPkt(ThreadVars *tv, |
86 | | DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p, |
87 | | DetectRunScratchpad *scratch); |
88 | | static inline void DetectRulePacketRules(ThreadVars * const tv, |
89 | | DetectEngineCtx * const de_ctx, DetectEngineThreadCtx * const det_ctx, |
90 | | Packet * const p, Flow * const pflow, const DetectRunScratchpad *scratch); |
91 | | static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx, |
92 | | DetectEngineThreadCtx *det_ctx, Packet *p, |
93 | | Flow *f, DetectRunScratchpad *scratch); |
94 | | static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
95 | | Packet *p, Flow *f, DetectRunScratchpad *scratch); |
96 | | static inline void DetectRunPostRules(ThreadVars *tv, DetectEngineCtx *de_ctx, |
97 | | DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow, |
98 | | DetectRunScratchpad *scratch); |
99 | | static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx, |
100 | | Packet *p, Flow * const pflow); |
101 | | |
102 | | /** \internal |
103 | | */ |
104 | | static void DetectRun(ThreadVars *th_v, |
105 | | DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
106 | | Packet *p) |
107 | 8.68M | { |
108 | 8.68M | SCEnter(); |
109 | 8.68M | SCLogDebug("p->pcap_cnt %" PRIu64 " direction %s pkt_src %s", p->pcap_cnt, |
110 | 8.68M | p->flow ? (FlowGetPacketDirection(p->flow, p) == TOSERVER ? "toserver" : "toclient") |
111 | 8.68M | : "noflow", |
112 | 8.68M | PktSrcToString(p->pkt_src)); |
113 | | |
114 | | /* bail early if packet should not be inspected */ |
115 | 8.68M | if (p->flags & PKT_NOPACKET_INSPECTION) { |
116 | | /* nothing to do */ |
117 | 0 | SCReturn; |
118 | 0 | } |
119 | | |
120 | | /* Load the Packet's flow early, even though it might not be needed. |
121 | | * Mark as a constant pointer, although the flow itself can change. */ |
122 | 8.68M | Flow * const pflow = p->flow; |
123 | | |
124 | 8.68M | DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow); |
125 | | |
126 | | /* run the IPonly engine */ |
127 | 8.68M | DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p); |
128 | | |
129 | | /* get our rule group */ |
130 | 8.68M | DetectRunGetRuleGroup(de_ctx, p, pflow, &scratch); |
131 | | /* if we didn't get a sig group head, we |
132 | | * have nothing to do.... */ |
133 | 8.68M | if (scratch.sgh == NULL) { |
134 | 6.16M | SCLogDebug("no sgh for this packet, nothing to match against"); |
135 | 6.16M | goto end; |
136 | 6.16M | } |
137 | | |
138 | | /* run the prefilters for packets */ |
139 | 2.51M | DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch); |
140 | | |
141 | 2.51M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES); |
142 | | /* inspect the rules against the packet */ |
143 | 2.51M | DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch); |
144 | 2.51M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES); |
145 | | |
146 | | /* run tx/state inspection. Don't call for ICMP error msgs. */ |
147 | 2.51M | if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) { |
148 | 1.70M | if (p->proto == IPPROTO_TCP) { |
149 | 1.64M | const TcpSession *ssn = p->flow->protoctx; |
150 | 1.64M | if (ssn && (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0) { |
151 | | // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX); |
152 | 1.48M | DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch); |
153 | | // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX); |
154 | 1.48M | } |
155 | | // no update to transactions |
156 | 1.64M | if (!PKT_IS_PSEUDOPKT(p) && p->app_update_direction == 0 && |
157 | 1.05M | ((PKT_IS_TOSERVER(p) && (p->flow->flags & FLOW_TS_APP_UPDATED) == 0) || |
158 | 895k | (PKT_IS_TOCLIENT(p) && (p->flow->flags & FLOW_TC_APP_UPDATED) == 0))) { |
159 | 895k | goto end; |
160 | 895k | } |
161 | 1.64M | } else if (p->proto == IPPROTO_UDP) { |
162 | 69.3k | DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch); |
163 | 69.3k | } |
164 | | |
165 | 813k | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX); |
166 | 813k | DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch); |
167 | 813k | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX); |
168 | | /* see if we need to increment the inspect_id and reset the de_state */ |
169 | 813k | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX_UPDATE); |
170 | 813k | AppLayerParserSetTransactionInspectId( |
171 | 813k | pflow, pflow->alparser, pflow->alstate, scratch.flow_flags, (scratch.sgh == NULL)); |
172 | 813k | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX_UPDATE); |
173 | 813k | } |
174 | | |
175 | 8.68M | end: |
176 | 8.68M | DetectRunPostRules(th_v, de_ctx, det_ctx, p, pflow, &scratch); |
177 | | |
178 | 8.68M | DetectRunCleanup(det_ctx, p, pflow); |
179 | 8.68M | SCReturn; |
180 | 2.51M | } |
181 | | |
182 | | static void DetectRunPostMatch(ThreadVars *tv, |
183 | | DetectEngineThreadCtx *det_ctx, Packet *p, |
184 | | const Signature *s) |
185 | 805k | { |
186 | | /* run the packet match functions */ |
187 | 805k | const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_POSTMATCH]; |
188 | 805k | if (smd != NULL) { |
189 | 98.7k | KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_POSTMATCH); |
190 | | |
191 | 98.7k | SCLogDebug("running match functions, sm %p", smd); |
192 | | |
193 | 106k | while (1) { |
194 | 106k | KEYWORD_PROFILING_START; |
195 | 106k | (void)sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx); |
196 | 106k | KEYWORD_PROFILING_END(det_ctx, smd->type, 1); |
197 | 106k | if (smd->is_last) |
198 | 98.7k | break; |
199 | 7.91k | smd++; |
200 | 7.91k | } |
201 | 98.7k | } |
202 | 805k | } |
203 | | |
204 | | /** |
205 | | * \brief Get the SigGroupHead for a packet. |
206 | | * |
207 | | * \param de_ctx detection engine context |
208 | | * \param p packet |
209 | | * |
210 | | * \retval sgh the SigGroupHead or NULL if non applies to the packet |
211 | | */ |
212 | | const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, |
213 | | const Packet *p) |
214 | 1.97M | { |
215 | 1.97M | SCEnter(); |
216 | | |
217 | 1.97M | int f; |
218 | 1.97M | SigGroupHead *sgh = NULL; |
219 | | |
220 | | /* if the packet proto is 0 (not set), we're inspecting it against |
221 | | * the decoder events sgh we have. */ |
222 | 1.97M | if (p->proto == 0 && p->events.cnt > 0) { |
223 | 710k | SCReturnPtr(de_ctx->decoder_event_sgh, "SigGroupHead"); |
224 | 1.26M | } else if (p->proto == 0) { |
225 | 526k | if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) { |
226 | | /* not IP, so nothing to do */ |
227 | 515k | SCReturnPtr(NULL, "SigGroupHead"); |
228 | 515k | } |
229 | 526k | } |
230 | | |
231 | | /* select the flow_gh */ |
232 | 746k | if (p->flowflags & FLOW_PKT_TOCLIENT) |
233 | 167k | f = 0; |
234 | 579k | else |
235 | 579k | f = 1; |
236 | | |
237 | 746k | int proto = IP_GET_IPPROTO(p); |
238 | 746k | if (proto == IPPROTO_TCP) { |
239 | 546k | DetectPort *list = de_ctx->flow_gh[f].tcp; |
240 | 546k | SCLogDebug("tcp toserver %p, tcp toclient %p: going to use %p", |
241 | 546k | de_ctx->flow_gh[1].tcp, de_ctx->flow_gh[0].tcp, de_ctx->flow_gh[f].tcp); |
242 | 546k | uint16_t port = f ? p->dp : p->sp; |
243 | 546k | SCLogDebug("tcp port %u -> %u:%u", port, p->sp, p->dp); |
244 | 546k | DetectPort *sghport = DetectPortLookupGroup(list, port); |
245 | 546k | if (sghport != NULL) |
246 | 204k | sgh = sghport->sh; |
247 | 546k | SCLogDebug("TCP list %p, port %u, direction %s, sghport %p, sgh %p", |
248 | 546k | list, port, f ? "toserver" : "toclient", sghport, sgh); |
249 | 546k | } else if (proto == IPPROTO_UDP) { |
250 | 39.8k | DetectPort *list = de_ctx->flow_gh[f].udp; |
251 | 39.8k | uint16_t port = f ? p->dp : p->sp; |
252 | 39.8k | DetectPort *sghport = DetectPortLookupGroup(list, port); |
253 | 39.8k | if (sghport != NULL) |
254 | 16.9k | sgh = sghport->sh; |
255 | 39.8k | SCLogDebug("UDP list %p, port %u, direction %s, sghport %p, sgh %p", |
256 | 39.8k | list, port, f ? "toserver" : "toclient", sghport, sgh); |
257 | 160k | } else { |
258 | 160k | sgh = de_ctx->flow_gh[f].sgh[proto]; |
259 | 160k | } |
260 | | |
261 | 746k | SCReturnPtr(sgh, "SigGroupHead"); |
262 | 1.97M | } |
263 | | |
264 | | static inline void DetectPrefilterMergeSort(DetectEngineCtx *de_ctx, |
265 | | DetectEngineThreadCtx *det_ctx) |
266 | 2.11M | { |
267 | 2.11M | SigIntId mpm, nonmpm; |
268 | 2.11M | SigIntId *mpm_ptr = det_ctx->pmq.rule_id_array; |
269 | 2.11M | SigIntId *nonmpm_ptr = det_ctx->non_pf_id_array; |
270 | 2.11M | uint32_t m_cnt = det_ctx->pmq.rule_id_array_cnt; |
271 | 2.11M | uint32_t n_cnt = det_ctx->non_pf_id_cnt; |
272 | 2.11M | SigIntId *final_ptr; |
273 | 2.11M | uint32_t final_cnt; |
274 | 2.11M | SigIntId id; |
275 | 2.11M | SigIntId previous_id = (SigIntId)-1; |
276 | 2.11M | Signature **sig_array = de_ctx->sig_array; |
277 | 2.11M | Signature **match_array = det_ctx->match_array; |
278 | 2.11M | Signature *s; |
279 | | |
280 | 2.11M | SCLogDebug("PMQ rule id array count %d", det_ctx->pmq.rule_id_array_cnt); |
281 | | |
282 | | /* Load first values. */ |
283 | 2.11M | if (likely(m_cnt)) { |
284 | 27.7k | mpm = *mpm_ptr; |
285 | 2.08M | } else { |
286 | | /* mpm list is empty */ |
287 | 2.08M | final_ptr = nonmpm_ptr; |
288 | 2.08M | final_cnt = n_cnt; |
289 | 2.08M | goto final; |
290 | 2.08M | } |
291 | 27.7k | if (likely(n_cnt)) { |
292 | 17.0k | nonmpm = *nonmpm_ptr; |
293 | 17.0k | } else { |
294 | | /* non-mpm list is empty. */ |
295 | 10.7k | final_ptr = mpm_ptr; |
296 | 10.7k | final_cnt = m_cnt; |
297 | 10.7k | goto final; |
298 | 10.7k | } |
299 | 30.1k | while (1) { |
300 | 30.1k | if (mpm < nonmpm) { |
301 | | /* Take from mpm list */ |
302 | 8.08k | id = mpm; |
303 | | |
304 | 8.08k | s = sig_array[id]; |
305 | | /* As the mpm list can contain duplicates, check for that here. */ |
306 | 8.08k | if (likely(id != previous_id)) { |
307 | 8.03k | *match_array++ = s; |
308 | 8.03k | previous_id = id; |
309 | 8.03k | } |
310 | 8.08k | if (unlikely(--m_cnt == 0)) { |
311 | | /* mpm list is now empty */ |
312 | 5.92k | final_ptr = nonmpm_ptr; |
313 | 5.92k | final_cnt = n_cnt; |
314 | 5.92k | goto final; |
315 | 5.92k | } |
316 | 2.15k | mpm_ptr++; |
317 | 2.15k | mpm = *mpm_ptr; |
318 | 22.0k | } else if (mpm > nonmpm) { |
319 | 21.5k | id = nonmpm; |
320 | | |
321 | 21.5k | s = sig_array[id]; |
322 | | /* As the mpm list can contain duplicates, check for that here. */ |
323 | 21.5k | if (likely(id != previous_id)) { |
324 | 21.5k | *match_array++ = s; |
325 | 21.5k | previous_id = id; |
326 | 21.5k | } |
327 | 21.5k | if (unlikely(--n_cnt == 0)) { |
328 | 10.6k | final_ptr = mpm_ptr; |
329 | 10.6k | final_cnt = m_cnt; |
330 | 10.6k | goto final; |
331 | 10.6k | } |
332 | 10.8k | nonmpm_ptr++; |
333 | 10.8k | nonmpm = *nonmpm_ptr; |
334 | | |
335 | 10.8k | } else { /* implied mpm == nonmpm */ |
336 | | /* special case: if on both lists, it's a negated mpm pattern */ |
337 | | |
338 | | /* mpm list may have dups, so skip past them here */ |
339 | 528 | while (--m_cnt != 0) { |
340 | 106 | mpm_ptr++; |
341 | 106 | mpm = *mpm_ptr; |
342 | 106 | if (mpm != nonmpm) |
343 | 105 | break; |
344 | 106 | } |
345 | | /* if mpm is done, update nonmpm_ptrs and jump to final */ |
346 | 527 | if (unlikely(m_cnt == 0)) { |
347 | 422 | n_cnt--; |
348 | | |
349 | | /* mpm list is now empty */ |
350 | 422 | final_ptr = ++nonmpm_ptr; |
351 | 422 | final_cnt = n_cnt; |
352 | 422 | goto final; |
353 | 422 | } |
354 | | /* otherwise, if nonmpm is done jump to final for mpm |
355 | | * mpm ptrs already updated */ |
356 | 105 | if (unlikely(--n_cnt == 0)) { |
357 | 28 | final_ptr = mpm_ptr; |
358 | 28 | final_cnt = m_cnt; |
359 | 28 | goto final; |
360 | 28 | } |
361 | | |
362 | | /* not at end of the lists, update nonmpm. Mpm already |
363 | | * updated in while loop above. */ |
364 | 77 | nonmpm_ptr++; |
365 | 77 | nonmpm = *nonmpm_ptr; |
366 | 77 | } |
367 | 30.1k | } |
368 | | |
369 | 2.11M | final: /* Only one list remaining. Just walk that list. */ |
370 | | |
371 | 4.93M | while (final_cnt-- > 0) { |
372 | 2.82M | id = *final_ptr++; |
373 | 2.82M | s = sig_array[id]; |
374 | | |
375 | | /* As the mpm list can contain duplicates, check for that here. */ |
376 | 2.82M | if (likely(id != previous_id)) { |
377 | 2.82M | *match_array++ = s; |
378 | 2.82M | previous_id = id; |
379 | 2.82M | } |
380 | 2.82M | } |
381 | | |
382 | 2.11M | det_ctx->match_array_cnt = match_array - det_ctx->match_array; |
383 | 2.11M | DEBUG_VALIDATE_BUG_ON((det_ctx->pmq.rule_id_array_cnt + det_ctx->non_pf_id_cnt) < det_ctx->match_array_cnt); |
384 | 2.11M | PMQ_RESET(&det_ctx->pmq); |
385 | 2.11M | } |
386 | | |
387 | | /** \internal |
388 | | * \brief build non-prefilter list based on the rule group list we've set. |
389 | | */ |
390 | | static inline void DetectPrefilterBuildNonPrefilterList( |
391 | | DetectEngineThreadCtx *det_ctx, const SignatureMask mask, const AppProto alproto) |
392 | 2.10M | { |
393 | 7.52M | for (uint32_t x = 0; x < det_ctx->non_pf_store_cnt; x++) { |
394 | | /* only if the mask matches this rule can possibly match, |
395 | | * so build the non_mpm array only for match candidates */ |
396 | 5.41M | const SignatureMask rule_mask = det_ctx->non_pf_store_ptr[x].mask; |
397 | 5.41M | const AppProto rule_alproto = det_ctx->non_pf_store_ptr[x].alproto; |
398 | 5.41M | if ((rule_mask & mask) == rule_mask && |
399 | 4.01M | (rule_alproto == 0 || AppProtoEquals(rule_alproto, alproto))) { |
400 | 2.82M | det_ctx->non_pf_id_array[det_ctx->non_pf_id_cnt++] = det_ctx->non_pf_store_ptr[x].id; |
401 | 2.82M | } |
402 | 5.41M | } |
403 | 2.10M | } |
404 | | |
405 | | /** \internal |
406 | | * \brief select non-mpm list |
407 | | * Based on the packet properties, select the non-mpm list to use |
408 | | * \todo move non_pf_store* into scratchpad */ |
409 | | static inline void |
410 | | DetectPrefilterSetNonPrefilterList(const Packet *p, DetectEngineThreadCtx *det_ctx, DetectRunScratchpad *scratch) |
411 | 2.51M | { |
412 | 2.51M | if ((p->proto == IPPROTO_TCP) && (p->tcph != NULL) && (p->tcph->th_flags & TH_SYN)) { |
413 | 134k | det_ctx->non_pf_store_ptr = scratch->sgh->non_pf_syn_store_array; |
414 | 134k | det_ctx->non_pf_store_cnt = scratch->sgh->non_pf_syn_store_cnt; |
415 | 2.38M | } else { |
416 | 2.38M | det_ctx->non_pf_store_ptr = scratch->sgh->non_pf_other_store_array; |
417 | 2.38M | det_ctx->non_pf_store_cnt = scratch->sgh->non_pf_other_store_cnt; |
418 | 2.38M | } |
419 | 2.51M | SCLogDebug("sgh non_pf ptr %p cnt %u (syn %p/%u, other %p/%u)", |
420 | 2.51M | det_ctx->non_pf_store_ptr, det_ctx->non_pf_store_cnt, |
421 | 2.51M | scratch->sgh->non_pf_syn_store_array, scratch->sgh->non_pf_syn_store_cnt, |
422 | 2.51M | scratch->sgh->non_pf_other_store_array, scratch->sgh->non_pf_other_store_cnt); |
423 | 2.51M | } |
424 | | |
425 | | /** \internal |
426 | | * \brief update flow's file tracking flags based on the detection engine |
427 | | * A set of flags is prepared that is sent to the File API. The |
428 | | File API may reject one or more based on the global force settings. |
429 | | */ |
430 | | static inline void |
431 | | DetectPostInspectFileFlagsUpdate(Flow *f, const SigGroupHead *sgh, uint8_t direction) |
432 | 404k | { |
433 | 404k | uint16_t flow_file_flags = FLOWFILE_INIT; |
434 | | |
435 | 404k | if (sgh == NULL) { |
436 | 248k | SCLogDebug("requesting disabling all file features for flow"); |
437 | 248k | flow_file_flags = FLOWFILE_NONE; |
438 | 248k | } else { |
439 | 156k | if (sgh->filestore_cnt == 0) { |
440 | 117k | SCLogDebug("requesting disabling filestore for flow"); |
441 | 117k | flow_file_flags |= (FLOWFILE_NO_STORE_TS|FLOWFILE_NO_STORE_TC); |
442 | 117k | } |
443 | | #ifdef HAVE_MAGIC |
444 | | if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMAGIC)) { |
445 | | SCLogDebug("requesting disabling magic for flow"); |
446 | | flow_file_flags |= (FLOWFILE_NO_MAGIC_TS|FLOWFILE_NO_MAGIC_TC); |
447 | | } |
448 | | #endif |
449 | 156k | if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILEMD5)) { |
450 | 156k | SCLogDebug("requesting disabling md5 for flow"); |
451 | 156k | flow_file_flags |= (FLOWFILE_NO_MD5_TS|FLOWFILE_NO_MD5_TC); |
452 | 156k | } |
453 | 156k | if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA1)) { |
454 | 156k | SCLogDebug("requesting disabling sha1 for flow"); |
455 | 156k | flow_file_flags |= (FLOWFILE_NO_SHA1_TS|FLOWFILE_NO_SHA1_TC); |
456 | 156k | } |
457 | 156k | if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESHA256)) { |
458 | 156k | SCLogDebug("requesting disabling sha256 for flow"); |
459 | 156k | flow_file_flags |= (FLOWFILE_NO_SHA256_TS|FLOWFILE_NO_SHA256_TC); |
460 | 156k | } |
461 | 156k | if (!(sgh->flags & SIG_GROUP_HEAD_HAVEFILESIZE)) { |
462 | 156k | SCLogDebug("requesting disabling filesize for flow"); |
463 | 156k | flow_file_flags |= (FLOWFILE_NO_SIZE_TS|FLOWFILE_NO_SIZE_TC); |
464 | 156k | } |
465 | 156k | } |
466 | 404k | if (flow_file_flags != 0) { |
467 | 404k | FileUpdateFlowFileFlags(f, flow_file_flags, direction); |
468 | 404k | } |
469 | 404k | } |
470 | | |
471 | | static inline void |
472 | | DetectRunPostGetFirstRuleGroup(const Packet *p, Flow *pflow, const SigGroupHead *sgh) |
473 | 845k | { |
474 | 845k | if ((p->flowflags & FLOW_PKT_TOSERVER) && !(pflow->flags & FLOW_SGH_TOSERVER)) { |
475 | | /* first time we see this toserver sgh, store it */ |
476 | 507k | pflow->sgh_toserver = sgh; |
477 | 507k | pflow->flags |= FLOW_SGH_TOSERVER; |
478 | | |
479 | 507k | if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) { |
480 | 321k | if (pflow->protoctx != NULL) { |
481 | 298k | TcpSession *ssn = pflow->protoctx; |
482 | 298k | SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.client"); |
483 | 298k | ssn->client.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW; |
484 | 298k | } |
485 | 321k | } |
486 | | |
487 | 507k | DetectPostInspectFileFlagsUpdate(pflow, |
488 | 507k | pflow->sgh_toserver, STREAM_TOSERVER); |
489 | | |
490 | 507k | } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && !(pflow->flags & FLOW_SGH_TOCLIENT)) { |
491 | 337k | pflow->sgh_toclient = sgh; |
492 | 337k | pflow->flags |= FLOW_SGH_TOCLIENT; |
493 | | |
494 | 337k | if (p->proto == IPPROTO_TCP && (sgh == NULL || !(sgh->flags & SIG_GROUP_HEAD_HAVERAWSTREAM))) { |
495 | 251k | if (pflow->protoctx != NULL) { |
496 | 250k | TcpSession *ssn = pflow->protoctx; |
497 | 250k | SCLogDebug("STREAMTCP_STREAM_FLAG_DISABLE_RAW ssn.server"); |
498 | 250k | ssn->server.flags |= STREAMTCP_STREAM_FLAG_DISABLE_RAW; |
499 | 250k | } |
500 | 251k | } |
501 | | |
502 | 337k | DetectPostInspectFileFlagsUpdate(pflow, |
503 | 337k | pflow->sgh_toclient, STREAM_TOCLIENT); |
504 | 337k | } |
505 | 845k | } |
506 | | |
507 | | static inline void DetectRunGetRuleGroup( |
508 | | const DetectEngineCtx *de_ctx, |
509 | | Packet * const p, Flow * const pflow, |
510 | | DetectRunScratchpad *scratch) |
511 | 8.68M | { |
512 | 8.68M | const SigGroupHead *sgh = NULL; |
513 | | |
514 | 8.68M | if (pflow) { |
515 | 7.11M | bool use_flow_sgh = false; |
516 | | /* Get the stored sgh from the flow (if any). Make sure we're not using |
517 | | * the sgh for icmp error packets part of the same stream. */ |
518 | 7.11M | if (IP_GET_IPPROTO(p) == pflow->proto) { /* filter out icmp */ |
519 | 7.11M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH); |
520 | 7.11M | if ((p->flowflags & FLOW_PKT_TOSERVER) && (pflow->flags & FLOW_SGH_TOSERVER)) { |
521 | 3.80M | sgh = pflow->sgh_toserver; |
522 | 3.80M | SCLogDebug("sgh = pflow->sgh_toserver; => %p", sgh); |
523 | 3.80M | use_flow_sgh = true; |
524 | 3.80M | } else if ((p->flowflags & FLOW_PKT_TOCLIENT) && (pflow->flags & FLOW_SGH_TOCLIENT)) { |
525 | 2.90M | sgh = pflow->sgh_toclient; |
526 | 2.90M | SCLogDebug("sgh = pflow->sgh_toclient; => %p", sgh); |
527 | 2.90M | use_flow_sgh = true; |
528 | 2.90M | } |
529 | 7.11M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH); |
530 | 7.11M | } |
531 | | |
532 | 7.11M | if (!(use_flow_sgh)) { |
533 | 405k | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH); |
534 | 405k | sgh = SigMatchSignaturesGetSgh(de_ctx, p); |
535 | 405k | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH); |
536 | | |
537 | | /* HACK: prevent the wrong sgh (or NULL) from being stored in the |
538 | | * flow's sgh pointers */ |
539 | 405k | if (PKT_IS_ICMPV4(p) && ICMPV4_DEST_UNREACH_IS_VALID(p)) { |
540 | 183 | ; /* no-op */ |
541 | 404k | } else { |
542 | | /* store the found sgh (or NULL) in the flow to save us |
543 | | * from looking it up again for the next packet. |
544 | | * Also run other tasks */ |
545 | 404k | DetectRunPostGetFirstRuleGroup(p, pflow, sgh); |
546 | 404k | } |
547 | 405k | } |
548 | 7.11M | } else { /* p->flags & PKT_HAS_FLOW */ |
549 | | /* no flow */ |
550 | | |
551 | 1.56M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_GETSGH); |
552 | 1.56M | sgh = SigMatchSignaturesGetSgh(de_ctx, p); |
553 | 1.56M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_GETSGH); |
554 | 1.56M | } |
555 | | |
556 | 8.68M | scratch->sgh = sgh; |
557 | 8.68M | } |
558 | | |
559 | | static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx, |
560 | | DetectEngineThreadCtx *det_ctx, |
561 | | Flow * const pflow, Packet * const p) |
562 | 17.6M | { |
563 | 17.6M | if (pflow) { |
564 | 14.2M | if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) { |
565 | 777k | SCLogDebug("testing against \"ip-only\" signatures"); |
566 | | |
567 | 777k | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY); |
568 | 777k | IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p); |
569 | 777k | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY); |
570 | 777k | } |
571 | 14.2M | } else { /* p->flags & PKT_HAS_FLOW */ |
572 | | /* no flow */ |
573 | | |
574 | | /* Even without flow we should match the packet src/dst */ |
575 | 3.40M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_IPONLY); |
576 | 3.40M | IPOnlyMatchPacket(tv, de_ctx, det_ctx, &de_ctx->io_ctx, p); |
577 | 3.40M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_IPONLY); |
578 | 3.40M | } |
579 | 17.6M | } |
580 | | |
581 | | /* returns 0 if no match, 1 if match */ |
582 | | static inline int DetectRunInspectRuleHeader( |
583 | | const Packet *p, |
584 | | const Flow *f, |
585 | | const Signature *s, |
586 | | const uint32_t sflags, |
587 | | const uint8_t s_proto_flags) |
588 | 2.54M | { |
589 | | /* check if this signature has a requirement for flowvars of some type |
590 | | * and if so, if we actually have any in the flow. If not, the sig |
591 | | * can't match and we skip it. */ |
592 | 2.54M | if ((p->flags & PKT_HAS_FLOW) && (sflags & SIG_FLAG_REQUIRE_FLOWVAR)) { |
593 | 29.5k | DEBUG_VALIDATE_BUG_ON(f == NULL); |
594 | | |
595 | 29.5k | int m = f->flowvar ? 1 : 0; |
596 | | |
597 | | /* no flowvars? skip this sig */ |
598 | 29.5k | if (m == 0) { |
599 | 18.5k | SCLogDebug("skipping sig as the flow has no flowvars and sig " |
600 | 18.5k | "has SIG_FLAG_REQUIRE_FLOWVAR flag set."); |
601 | 18.5k | return 0; |
602 | 18.5k | } |
603 | 29.5k | } |
604 | | |
605 | 2.52M | if ((s_proto_flags & DETECT_PROTO_IPV4) && !PKT_IS_IPV4(p)) { |
606 | 868 | SCLogDebug("ip version didn't match"); |
607 | 868 | return 0; |
608 | 868 | } |
609 | 2.52M | if ((s_proto_flags & DETECT_PROTO_IPV6) && !PKT_IS_IPV6(p)) { |
610 | 31.8k | SCLogDebug("ip version didn't match"); |
611 | 31.8k | return 0; |
612 | 31.8k | } |
613 | | |
614 | 2.49M | if (DetectProtoContainsProto(&s->proto, IP_GET_IPPROTO(p)) == 0) { |
615 | 324 | SCLogDebug("proto didn't match"); |
616 | 324 | return 0; |
617 | 324 | } |
618 | | |
619 | | /* check the source & dst port in the sig */ |
620 | 2.49M | if (p->proto == IPPROTO_TCP || p->proto == IPPROTO_UDP || p->proto == IPPROTO_SCTP) { |
621 | 2.07M | if (!(sflags & SIG_FLAG_DP_ANY)) { |
622 | 49.3k | if (p->flags & PKT_IS_FRAGMENT) |
623 | 0 | return 0; |
624 | 49.3k | DetectPort *dport = DetectPortLookupGroup(s->dp,p->dp); |
625 | 49.3k | if (dport == NULL) { |
626 | 27.6k | SCLogDebug("dport didn't match."); |
627 | 27.6k | return 0; |
628 | 27.6k | } |
629 | 49.3k | } |
630 | 2.04M | if (!(sflags & SIG_FLAG_SP_ANY)) { |
631 | 34.3k | if (p->flags & PKT_IS_FRAGMENT) |
632 | 11 | return 0; |
633 | 34.3k | DetectPort *sport = DetectPortLookupGroup(s->sp,p->sp); |
634 | 34.3k | if (sport == NULL) { |
635 | 31.3k | SCLogDebug("sport didn't match."); |
636 | 31.3k | return 0; |
637 | 31.3k | } |
638 | 34.3k | } |
639 | 2.04M | } else if ((sflags & (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) != (SIG_FLAG_DP_ANY|SIG_FLAG_SP_ANY)) { |
640 | 3.45k | SCLogDebug("port-less protocol and sig needs ports"); |
641 | 3.45k | return 0; |
642 | 3.45k | } |
643 | | |
644 | | /* check the destination address */ |
645 | 2.42M | if (!(sflags & SIG_FLAG_DST_ANY)) { |
646 | 8.61k | if (PKT_IS_IPV4(p)) { |
647 | 6.98k | if (DetectAddressMatchIPv4(s->addr_dst_match4, s->addr_dst_match4_cnt, &p->dst) == 0) |
648 | 1.86k | return 0; |
649 | 6.98k | } else if (PKT_IS_IPV6(p)) { |
650 | 1.63k | if (DetectAddressMatchIPv6(s->addr_dst_match6, s->addr_dst_match6_cnt, &p->dst) == 0) |
651 | 81 | return 0; |
652 | 1.63k | } |
653 | 8.61k | } |
654 | | /* check the source address */ |
655 | 2.42M | if (!(sflags & SIG_FLAG_SRC_ANY)) { |
656 | 4.00k | if (PKT_IS_IPV4(p)) { |
657 | 3.52k | if (DetectAddressMatchIPv4(s->addr_src_match4, s->addr_src_match4_cnt, &p->src) == 0) |
658 | 1.80k | return 0; |
659 | 3.52k | } else if (PKT_IS_IPV6(p)) { |
660 | 483 | if (DetectAddressMatchIPv6(s->addr_src_match6, s->addr_src_match6_cnt, &p->src) == 0) |
661 | 93 | return 0; |
662 | 483 | } |
663 | 4.00k | } |
664 | | |
665 | 2.42M | return 1; |
666 | 2.42M | } |
667 | | |
668 | | /** \internal |
669 | | * \brief run packet/stream prefilter engines |
670 | | */ |
671 | | static inline void DetectRunPrefilterPkt( |
672 | | ThreadVars *tv, |
673 | | DetectEngineCtx *de_ctx, |
674 | | DetectEngineThreadCtx *det_ctx, |
675 | | Packet *p, |
676 | | DetectRunScratchpad *scratch |
677 | | ) |
678 | 2.51M | { |
679 | 2.51M | DetectPrefilterSetNonPrefilterList(p, det_ctx, scratch); |
680 | | |
681 | | /* create our prefilter mask */ |
682 | 2.51M | PacketCreateMask(p, &scratch->pkt_mask, scratch->alproto, scratch->app_decoder_events); |
683 | | |
684 | | /* build and prefilter non_pf list against the mask of the packet */ |
685 | 2.51M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_NONMPMLIST); |
686 | 2.51M | det_ctx->non_pf_id_cnt = 0; |
687 | 2.51M | if (likely(det_ctx->non_pf_store_cnt > 0)) { |
688 | 2.10M | DetectPrefilterBuildNonPrefilterList(det_ctx, scratch->pkt_mask, scratch->alproto); |
689 | 2.10M | } |
690 | 2.51M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_NONMPMLIST); |
691 | | |
692 | | /* run the prefilter engines */ |
693 | 2.51M | Prefilter(det_ctx, scratch->sgh, p, scratch->flow_flags); |
694 | | /* create match list if we have non-pf and/or pf */ |
695 | 2.51M | if (det_ctx->non_pf_store_cnt || det_ctx->pmq.rule_id_array_cnt) { |
696 | | #ifdef PROFILING |
697 | | if (tv) { |
698 | | StatsAddUI64(tv, det_ctx->counter_mpm_list, (uint64_t)det_ctx->pmq.rule_id_array_cnt); |
699 | | } |
700 | | #endif |
701 | 2.11M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT2); |
702 | 2.11M | DetectPrefilterMergeSort(de_ctx, det_ctx); |
703 | 2.11M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT2); |
704 | 2.11M | } |
705 | | |
706 | | #ifdef PROFILING |
707 | | if (tv) { |
708 | | StatsAddUI64(tv, det_ctx->counter_nonmpm_list, |
709 | | (uint64_t)det_ctx->non_pf_store_cnt); |
710 | | /* non mpm sigs after mask prefilter */ |
711 | | StatsAddUI64(tv, det_ctx->counter_fnonmpm_list, |
712 | | (uint64_t)det_ctx->non_pf_id_cnt); |
713 | | } |
714 | | #endif |
715 | 2.51M | } |
716 | | |
717 | | static inline void DetectRulePacketRules( |
718 | | ThreadVars * const tv, |
719 | | DetectEngineCtx * const de_ctx, |
720 | | DetectEngineThreadCtx * const det_ctx, |
721 | | Packet * const p, |
722 | | Flow * const pflow, |
723 | | const DetectRunScratchpad *scratch |
724 | | ) |
725 | 2.51M | { |
726 | 2.51M | const Signature *s = NULL; |
727 | 2.51M | const Signature *next_s = NULL; |
728 | | |
729 | | /* inspect the sigs against the packet */ |
730 | | /* Prefetch the next signature. */ |
731 | 2.51M | SigIntId match_cnt = det_ctx->match_array_cnt; |
732 | | #ifdef PROFILING |
733 | | if (tv) { |
734 | | StatsAddUI64(tv, det_ctx->counter_match_list, |
735 | | (uint64_t)match_cnt); |
736 | | } |
737 | | #endif |
738 | 2.51M | Signature **match_array = det_ctx->match_array; |
739 | | |
740 | 2.51M | SGH_PROFILING_RECORD(det_ctx, scratch->sgh); |
741 | | #ifdef PROFILING |
742 | | if (match_cnt >= de_ctx->profile_match_logging_threshold) |
743 | | RulesDumpMatchArray(det_ctx, scratch->sgh, p); |
744 | | #endif |
745 | | |
746 | 2.51M | uint32_t sflags, next_sflags = 0; |
747 | 2.51M | if (match_cnt) { |
748 | 1.47M | next_s = *match_array++; |
749 | 1.47M | next_sflags = next_s->flags; |
750 | 1.47M | } |
751 | 5.37M | while (match_cnt--) { |
752 | 2.85M | RULE_PROFILING_START(p); |
753 | 2.85M | uint8_t alert_flags = 0; |
754 | | #ifdef PROFILE_RULES |
755 | | bool smatch = false; /* signature match */ |
756 | | #endif |
757 | 2.85M | s = next_s; |
758 | 2.85M | sflags = next_sflags; |
759 | 2.85M | if (match_cnt) { |
760 | 1.37M | next_s = *match_array++; |
761 | 1.37M | next_sflags = next_s->flags; |
762 | 1.37M | } |
763 | 2.85M | const uint8_t s_proto_flags = s->proto.flags; |
764 | | |
765 | 2.85M | SCLogDebug("inspecting signature id %"PRIu32"", s->id); |
766 | | |
767 | 2.85M | if (s->app_inspect != NULL) { |
768 | 590k | goto next; // handle sig in DetectRunTx |
769 | 590k | } |
770 | 2.26M | if (s->frame_inspect != NULL) { |
771 | 59.7k | goto next; // handle sig in DetectRunFrame |
772 | 59.7k | } |
773 | | |
774 | | /* skip pkt sigs for flow end packets */ |
775 | 2.20M | if ((p->flags & PKT_PSEUDO_STREAM_END) != 0 && s->type == SIG_TYPE_PKT) |
776 | 11.3k | goto next; |
777 | | |
778 | | /* don't run mask check for stateful rules. |
779 | | * There we depend on prefilter */ |
780 | 2.19M | if ((s->mask & scratch->pkt_mask) != s->mask) { |
781 | 631 | SCLogDebug("mask mismatch %x & %x != %x", s->mask, scratch->pkt_mask, s->mask); |
782 | 631 | goto next; |
783 | 631 | } |
784 | | |
785 | 2.19M | if (SigDsizePrefilter(p, s, sflags)) |
786 | 9.75k | goto next; |
787 | | |
788 | | /* if the sig has alproto and the session as well they should match */ |
789 | 2.18M | if (likely(sflags & SIG_FLAG_APPLAYER)) { |
790 | 10.0k | if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, scratch->alproto)) { |
791 | 690 | SCLogDebug("alproto mismatch"); |
792 | 690 | goto next; |
793 | 690 | } |
794 | 10.0k | } |
795 | | |
796 | 2.18M | if (DetectRunInspectRuleHeader(p, pflow, s, sflags, s_proto_flags) == 0) { |
797 | 112k | goto next; |
798 | 112k | } |
799 | | |
800 | 2.06M | if (DetectEnginePktInspectionRun(tv, det_ctx, s, pflow, p, &alert_flags) == false) { |
801 | 1.71M | goto next; |
802 | 1.71M | } |
803 | | |
804 | | #ifdef PROFILE_RULES |
805 | | smatch = true; |
806 | | #endif |
807 | 356k | DetectRunPostMatch(tv, det_ctx, p, s); |
808 | | |
809 | 356k | uint64_t txid = PACKET_ALERT_NOTX; |
810 | 356k | if (pflow && pflow->alstate) { |
811 | 231k | uint8_t dir = (p->flowflags & FLOW_PKT_TOCLIENT) ? STREAM_TOCLIENT : STREAM_TOSERVER; |
812 | 231k | txid = AppLayerParserGetTransactionInspectId(pflow->alparser, dir); |
813 | 231k | if ((s->alproto != ALPROTO_UNKNOWN && pflow->proto == IPPROTO_UDP) || |
814 | 231k | (alert_flags & PACKET_ALERT_FLAG_STREAM_MATCH) || |
815 | 203k | (de_ctx->guess_applayer && |
816 | 28.1k | AppLayerParserGetTxCnt(pflow, pflow->alstate) == txid + 1)) { |
817 | | // if there is a UDP specific app-layer signature, |
818 | | // or only one live transaction |
819 | | // try to use the good tx for the packet direction |
820 | 28.1k | alert_flags |= PACKET_ALERT_FLAG_TX; |
821 | 28.1k | if (pflow->proto != IPPROTO_UDP) { |
822 | 28.0k | alert_flags |= PACKET_ALERT_FLAG_TX_GUESSED; |
823 | 28.0k | } |
824 | 28.1k | } |
825 | 231k | } |
826 | 356k | AlertQueueAppend(det_ctx, s, p, txid, alert_flags); |
827 | 2.85M | next: |
828 | 2.85M | DetectVarProcessList(det_ctx, pflow, p); |
829 | 2.85M | DetectReplaceFree(det_ctx); |
830 | 2.85M | RULE_PROFILING_END(det_ctx, s, smatch, p); |
831 | | |
832 | 2.85M | det_ctx->flags = 0; |
833 | 2.85M | continue; |
834 | 356k | } |
835 | 2.51M | } |
836 | | |
837 | | static DetectRunScratchpad DetectRunSetup( |
838 | | const DetectEngineCtx *de_ctx, |
839 | | DetectEngineThreadCtx *det_ctx, |
840 | | Packet * const p, Flow * const pflow) |
841 | 17.6M | { |
842 | 17.6M | AppProto alproto = ALPROTO_UNKNOWN; |
843 | 17.6M | uint8_t flow_flags = 0; /* flow/state flags */ |
844 | 17.6M | bool app_decoder_events = false; |
845 | | |
846 | 17.6M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_SETUP); |
847 | | |
848 | | #ifdef UNITTESTS |
849 | | p->alerts.cnt = 0; |
850 | | p->alerts.discarded = 0; |
851 | | p->alerts.suppressed = 0; |
852 | | #endif |
853 | 17.6M | det_ctx->filestore_cnt = 0; |
854 | 17.6M | det_ctx->base64_decoded_len = 0; |
855 | 17.6M | det_ctx->raw_stream_progress = 0; |
856 | 17.6M | det_ctx->match_array_cnt = 0; |
857 | | |
858 | 17.6M | det_ctx->alert_queue_size = 0; |
859 | 17.6M | p->alerts.drop.action = 0; |
860 | | |
861 | | #ifdef DEBUG |
862 | | if (p->flags & PKT_STREAM_ADD) { |
863 | | det_ctx->pkt_stream_add_cnt++; |
864 | | } |
865 | | #endif |
866 | | |
867 | | /* grab the protocol state we will detect on */ |
868 | 17.6M | if (p->flags & PKT_HAS_FLOW) { |
869 | 14.2M | DEBUG_VALIDATE_BUG_ON(pflow == NULL); |
870 | | |
871 | 14.2M | if (p->flowflags & FLOW_PKT_TOSERVER) { |
872 | 8.14M | flow_flags = STREAM_TOSERVER; |
873 | 8.14M | SCLogDebug("flag STREAM_TOSERVER set"); |
874 | 8.14M | } else if (p->flowflags & FLOW_PKT_TOCLIENT) { |
875 | 6.08M | flow_flags = STREAM_TOCLIENT; |
876 | 6.08M | SCLogDebug("flag STREAM_TOCLIENT set"); |
877 | 6.08M | } |
878 | 14.2M | SCLogDebug("p->flowflags 0x%02x", p->flowflags); |
879 | | |
880 | 14.2M | if (p->flags & PKT_STREAM_EOF) { |
881 | 255k | flow_flags |= STREAM_EOF; |
882 | 255k | SCLogDebug("STREAM_EOF set"); |
883 | 255k | } |
884 | | |
885 | | /* store tenant_id in the flow so that we can use it |
886 | | * for creating pseudo packets */ |
887 | 14.2M | if (p->tenant_id > 0 && pflow->tenant_id == 0) { |
888 | 0 | pflow->tenant_id = p->tenant_id; |
889 | 0 | } |
890 | | |
891 | | /* live ruleswap check for flow updates */ |
892 | 14.2M | if (pflow->de_ctx_version == 0) { |
893 | | /* first time this flow is inspected, set id */ |
894 | 493k | pflow->de_ctx_version = de_ctx->version; |
895 | 13.7M | } else if (pflow->de_ctx_version != de_ctx->version) { |
896 | | /* first time we inspect flow with this de_ctx, reset */ |
897 | 30.7k | pflow->flags &= ~FLOW_SGH_TOSERVER; |
898 | 30.7k | pflow->flags &= ~FLOW_SGH_TOCLIENT; |
899 | 30.7k | pflow->sgh_toserver = NULL; |
900 | 30.7k | pflow->sgh_toclient = NULL; |
901 | | |
902 | 30.7k | pflow->de_ctx_version = de_ctx->version; |
903 | 30.7k | GenericVarFree(pflow->flowvar); |
904 | 30.7k | pflow->flowvar = NULL; |
905 | | |
906 | 30.7k | DetectEngineStateResetTxs(pflow); |
907 | 30.7k | } |
908 | | |
909 | | /* Retrieve the app layer state and protocol and the tcp reassembled |
910 | | * stream chunks. */ |
911 | 14.2M | if ((p->proto == IPPROTO_TCP && (p->flags & PKT_STREAM_EST)) || |
912 | 2.70M | (p->proto == IPPROTO_UDP) || |
913 | 1.87M | (p->proto == IPPROTO_SCTP && (p->flowflags & FLOW_PKT_ESTABLISHED))) |
914 | 12.3M | { |
915 | | /* update flow flags with knowledge on disruptions */ |
916 | 12.3M | flow_flags = FlowGetDisruptionFlags(pflow, flow_flags); |
917 | 12.3M | alproto = FlowGetAppProtocol(pflow); |
918 | 12.3M | if (p->proto == IPPROTO_TCP && pflow->protoctx && |
919 | 11.5M | StreamReassembleRawHasDataReady(pflow->protoctx, p)) { |
920 | 297k | p->flags |= PKT_DETECT_HAS_STREAMDATA; |
921 | 297k | } |
922 | 12.3M | SCLogDebug("alproto %u", alproto); |
923 | 12.3M | } else { |
924 | 1.87M | SCLogDebug("packet doesn't have established flag set (proto %d)", p->proto); |
925 | 1.87M | } |
926 | | |
927 | 14.2M | app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser); |
928 | 14.2M | } |
929 | | |
930 | 17.6M | DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, NULL, 0 }; |
931 | 17.6M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP); |
932 | 17.6M | return pad; |
933 | 17.6M | } |
934 | | |
935 | | static inline void DetectRunPostRules( |
936 | | ThreadVars *tv, |
937 | | DetectEngineCtx *de_ctx, |
938 | | DetectEngineThreadCtx *det_ctx, |
939 | | Packet * const p, |
940 | | Flow * const pflow, |
941 | | DetectRunScratchpad *scratch) |
942 | 8.68M | { |
943 | | /* so now let's iterate the alerts and remove the ones after a pass rule |
944 | | * matched (if any). This is done inside PacketAlertFinalize() */ |
945 | | /* PR: installed "tag" keywords are handled after the threshold inspection */ |
946 | | |
947 | 8.68M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_ALERT); |
948 | 8.68M | PacketAlertFinalize(de_ctx, det_ctx, p); |
949 | 8.68M | if (p->alerts.cnt > 0) { |
950 | 388k | StatsAddUI64(tv, det_ctx->counter_alerts, (uint64_t)p->alerts.cnt); |
951 | 388k | } |
952 | 8.68M | if (p->alerts.discarded > 0) { |
953 | 213 | StatsAddUI64(tv, det_ctx->counter_alerts_overflow, (uint64_t)p->alerts.discarded); |
954 | 213 | } |
955 | 8.68M | if (p->alerts.suppressed > 0) { |
956 | 6.99k | StatsAddUI64(tv, det_ctx->counter_alerts_suppressed, (uint64_t)p->alerts.suppressed); |
957 | 6.99k | } |
958 | 8.68M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_ALERT); |
959 | 8.68M | } |
960 | | |
961 | | static void DetectRunCleanup(DetectEngineThreadCtx *det_ctx, |
962 | | Packet *p, Flow * const pflow) |
963 | 17.6M | { |
964 | 17.6M | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_CLEANUP); |
965 | 17.6M | InspectionBufferClean(det_ctx); |
966 | | |
967 | 17.6M | if (pflow != NULL) { |
968 | | /* update inspected tracker for raw reassembly */ |
969 | 14.2M | if (p->proto == IPPROTO_TCP && pflow->protoctx != NULL && |
970 | 12.9M | (p->flags & PKT_DETECT_HAS_STREAMDATA)) { |
971 | 297k | StreamReassembleRawUpdateProgress(pflow->protoctx, p, |
972 | 297k | det_ctx->raw_stream_progress); |
973 | 297k | } |
974 | 14.2M | } |
975 | 17.6M | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_CLEANUP); |
976 | 17.6M | SCReturn; |
977 | 17.6M | } |
978 | | |
979 | | void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size) |
980 | 52.0k | { |
981 | 52.0k | DEBUG_VALIDATE_BUG_ON(det_ctx->tx_candidates); |
982 | 52.0k | det_ctx->tx_candidates = SCCalloc(size, sizeof(RuleMatchCandidateTx)); |
983 | 52.0k | if (det_ctx->tx_candidates == NULL) { |
984 | 0 | FatalError("failed to allocate %" PRIu64 " bytes", |
985 | 0 | (uint64_t)(size * sizeof(RuleMatchCandidateTx))); |
986 | 0 | } |
987 | 52.0k | det_ctx->tx_candidates_size = size; |
988 | 52.0k | SCLogDebug("array initialized to %u elements (%"PRIu64" bytes)", |
989 | 52.0k | size, (uint64_t)(size * sizeof(RuleMatchCandidateTx))); |
990 | 52.0k | } |
991 | | |
992 | | void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx) |
993 | 122k | { |
994 | 122k | SCFree(det_ctx->tx_candidates); |
995 | 122k | det_ctx->tx_candidates_size = 0; |
996 | 122k | } |
997 | | |
998 | | /* if size >= cur_space */ |
999 | | static inline bool RuleMatchCandidateTxArrayHasSpace(const DetectEngineThreadCtx *det_ctx, |
1000 | | const uint32_t need) |
1001 | 2.21M | { |
1002 | 2.21M | if (det_ctx->tx_candidates_size >= need) |
1003 | 2.20M | return 1; |
1004 | 1.48k | return 0; |
1005 | 2.21M | } |
1006 | | |
1007 | | /* realloc */ |
1008 | | static int RuleMatchCandidateTxArrayExpand(DetectEngineThreadCtx *det_ctx, const uint32_t needed) |
1009 | 1.48k | { |
1010 | 1.48k | const uint32_t old_size = det_ctx->tx_candidates_size; |
1011 | 1.48k | uint32_t new_size = needed; |
1012 | 1.48k | void *ptmp = SCRealloc(det_ctx->tx_candidates, (new_size * sizeof(RuleMatchCandidateTx))); |
1013 | 1.48k | if (ptmp == NULL) { |
1014 | 0 | FatalError("failed to expand to %" PRIu64 " bytes", |
1015 | 0 | (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); |
1016 | | // TODO can this be handled more gracefully? |
1017 | 0 | } |
1018 | 1.48k | det_ctx->tx_candidates = ptmp; |
1019 | 1.48k | det_ctx->tx_candidates_size = new_size; |
1020 | 1.48k | SCLogDebug("array expanded from %u to %u elements (%"PRIu64" bytes -> %"PRIu64" bytes)", |
1021 | 1.48k | old_size, new_size, (uint64_t)(old_size * sizeof(RuleMatchCandidateTx)), |
1022 | 1.48k | (uint64_t)(new_size * sizeof(RuleMatchCandidateTx))); (void)old_size; |
1023 | 1.48k | return 1; |
1024 | 1.48k | } |
1025 | | |
1026 | | /** \internal |
1027 | | * \brief sort helper for sorting match candidates by id: ascending |
1028 | | * |
1029 | | * The id field is set from Signature::num, so we sort the candidates to match the signature |
1030 | | * sort order (ascending), where candidates that have flags go first. |
1031 | | */ |
1032 | | static int |
1033 | | DetectRunTxSortHelper(const void *a, const void *b) |
1034 | 90.6k | { |
1035 | 90.6k | const RuleMatchCandidateTx *s0 = a; |
1036 | 90.6k | const RuleMatchCandidateTx *s1 = b; |
1037 | 90.6k | if (s1->id == s0->id) { |
1038 | 75.1k | if (s1->flags && !s0->flags) |
1039 | 74.1k | return 1; |
1040 | 954 | else if (!s1->flags && s0->flags) |
1041 | 0 | return -1; |
1042 | 954 | return 0; |
1043 | 75.1k | } else |
1044 | 15.5k | return s0->id > s1->id ? 1 : -1; |
1045 | 90.6k | } |
1046 | | |
1047 | | #if 0 |
1048 | | #define TRACE_SID_TXS(sid,txs,...) \ |
1049 | | do { \ |
1050 | | char _trace_buf[2048]; \ |
1051 | | snprintf(_trace_buf, sizeof(_trace_buf), __VA_ARGS__); \ |
1052 | | SCLogNotice("%p/%"PRIu64"/%u: %s", txs->tx_ptr, txs->tx_id, sid, _trace_buf); \ |
1053 | | } while(0) |
1054 | | #else |
1055 | | #define TRACE_SID_TXS(sid,txs,...) |
1056 | | #endif |
1057 | | |
1058 | | /** \internal |
1059 | | * \brief inspect a rule against a transaction |
1060 | | * |
1061 | | * Inspect a rule. New detection or continued stateful |
1062 | | * detection. |
1063 | | * |
1064 | | * \param stored_flags pointer to stored flags or NULL. |
1065 | | * If stored_flags is set it means we're continuing |
1066 | | * inspection from an earlier run. |
1067 | | * |
1068 | | * \retval bool true sig matched, false didn't match |
1069 | | */ |
1070 | | static bool DetectRunTxInspectRule(ThreadVars *tv, |
1071 | | DetectEngineCtx *de_ctx, |
1072 | | DetectEngineThreadCtx *det_ctx, |
1073 | | Packet *p, |
1074 | | Flow *f, |
1075 | | const uint8_t in_flow_flags, // direction, EOF, etc |
1076 | | void *alstate, |
1077 | | DetectTransaction *tx, |
1078 | | const Signature *s, |
1079 | | uint32_t *stored_flags, |
1080 | | RuleMatchCandidateTx *can, |
1081 | | DetectRunScratchpad *scratch) |
1082 | 288k | { |
1083 | 288k | uint8_t flow_flags = in_flow_flags; |
1084 | 288k | const int direction = (flow_flags & STREAM_TOSERVER) ? 0 : 1; |
1085 | 288k | uint32_t inspect_flags = stored_flags ? *stored_flags : 0; |
1086 | 288k | int total_matches = 0; |
1087 | 288k | uint16_t file_no_match = 0; |
1088 | 288k | bool retval = false; |
1089 | 288k | bool mpm_before_progress = false; // is mpm engine before progress? |
1090 | 288k | bool mpm_in_progress = false; // is mpm engine in a buffer we will revisit? |
1091 | | |
1092 | 288k | TRACE_SID_TXS(s->id, tx, "starting %s", direction ? "toclient" : "toserver"); |
1093 | | |
1094 | | /* for a new inspection we inspect pkt header and packet matches */ |
1095 | 288k | if (likely(stored_flags == NULL)) { |
1096 | 250k | TRACE_SID_TXS(s->id, tx, "first inspect, run packet matches"); |
1097 | 250k | if (DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags) == 0) { |
1098 | 5.82k | TRACE_SID_TXS(s->id, tx, "DetectRunInspectRuleHeader() no match"); |
1099 | 5.82k | return false; |
1100 | 5.82k | } |
1101 | 245k | if (DetectEnginePktInspectionRun(tv, det_ctx, s, f, p, NULL) == false) { |
1102 | 145 | TRACE_SID_TXS(s->id, tx, "DetectEnginePktInspectionRun no match"); |
1103 | 145 | return false; |
1104 | 145 | } |
1105 | | /* stream mpm and negated mpm sigs can end up here with wrong proto */ |
1106 | 244k | if (!(AppProtoEquals(s->alproto, f->alproto) || s->alproto == ALPROTO_UNKNOWN)) { |
1107 | 1 | TRACE_SID_TXS(s->id, tx, "alproto mismatch"); |
1108 | 1 | return false; |
1109 | 1 | } |
1110 | 244k | } |
1111 | | |
1112 | 282k | const DetectEngineAppInspectionEngine *engine = s->app_inspect; |
1113 | 1.59M | while (engine != NULL) { // TODO could be do {} while as s->app_inspect cannot be null |
1114 | 1.51M | TRACE_SID_TXS(s->id, tx, "engine %p inspect_flags %x", engine, inspect_flags); |
1115 | 1.51M | if (!(inspect_flags & BIT_U32(engine->id)) && |
1116 | 1.47M | direction == engine->dir) |
1117 | 836k | { |
1118 | 836k | const bool skip_engine = (engine->alproto != 0 && engine->alproto != f->alproto); |
1119 | | /* special case: file_data on 'alert tcp' will have engines |
1120 | | * in the list that are not for us. */ |
1121 | 836k | if (unlikely(skip_engine)) { |
1122 | 539k | engine = engine->next; |
1123 | 539k | continue; |
1124 | 539k | } |
1125 | | |
1126 | | /* engines are sorted per progress, except that the one with |
1127 | | * mpm/prefilter enabled is first */ |
1128 | 297k | if (tx->tx_progress < engine->progress) { |
1129 | 26.1k | SCLogDebug("tx progress %d < engine progress %d", |
1130 | 26.1k | tx->tx_progress, engine->progress); |
1131 | 26.1k | break; |
1132 | 26.1k | } |
1133 | 271k | if (engine->mpm) { |
1134 | 182k | if (tx->tx_progress > engine->progress) { |
1135 | 119k | TRACE_SID_TXS(s->id, tx, |
1136 | 119k | "engine->mpm: t->tx_progress %u > engine->progress %u, so set " |
1137 | 119k | "mpm_before_progress", |
1138 | 119k | tx->tx_progress, engine->progress); |
1139 | 119k | mpm_before_progress = true; |
1140 | 119k | } else if (tx->tx_progress == engine->progress) { |
1141 | 62.8k | TRACE_SID_TXS(s->id, tx, |
1142 | 62.8k | "engine->mpm: t->tx_progress %u == engine->progress %u, so set " |
1143 | 62.8k | "mpm_in_progress", |
1144 | 62.8k | tx->tx_progress, engine->progress); |
1145 | 62.8k | mpm_in_progress = true; |
1146 | 62.8k | } |
1147 | 182k | } |
1148 | | |
1149 | | /* run callback: but bypass stream callback if we can */ |
1150 | 271k | uint8_t match; |
1151 | 271k | if (unlikely(engine->stream && can->stream_stored)) { |
1152 | 0 | match = can->stream_result; |
1153 | 0 | TRACE_SID_TXS(s->id, tx, "stream skipped, stored result %d used instead", match); |
1154 | 271k | } else { |
1155 | 271k | KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list); |
1156 | 271k | DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL); |
1157 | 271k | match = engine->v2.Callback( |
1158 | 271k | de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx->tx_ptr, tx->tx_id); |
1159 | 271k | TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match); |
1160 | 271k | if (engine->stream) { |
1161 | 125k | can->stream_stored = true; |
1162 | 125k | can->stream_result = match; |
1163 | 125k | TRACE_SID_TXS(s->id, tx, "stream ran, store result %d for next tx (if any)", match); |
1164 | 125k | } |
1165 | 271k | } |
1166 | 271k | if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) { |
1167 | 93.6k | inspect_flags |= BIT_U32(engine->id); |
1168 | 93.6k | engine = engine->next; |
1169 | 93.6k | total_matches++; |
1170 | 93.6k | continue; |
1171 | 177k | } else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) { |
1172 | | /* if the file engine matched, but indicated more |
1173 | | * files are still in progress, we don't set inspect |
1174 | | * flags as these would end inspection for this tx */ |
1175 | 0 | engine = engine->next; |
1176 | 0 | total_matches++; |
1177 | 0 | continue; |
1178 | 177k | } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) { |
1179 | 26.2k | inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH; |
1180 | 26.2k | inspect_flags |= BIT_U32(engine->id); |
1181 | 151k | } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES) { |
1182 | 10.8k | inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH; |
1183 | 10.8k | inspect_flags |= BIT_U32(engine->id); |
1184 | 10.8k | file_no_match = 1; |
1185 | 10.8k | } |
1186 | | /* implied DETECT_ENGINE_INSPECT_SIG_NO_MATCH */ |
1187 | 177k | if (engine->mpm && mpm_before_progress) { |
1188 | 64.7k | inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH; |
1189 | 64.7k | inspect_flags |= BIT_U32(engine->id); |
1190 | 64.7k | } |
1191 | 177k | break; |
1192 | 271k | } |
1193 | 680k | engine = engine->next; |
1194 | 680k | } |
1195 | 282k | TRACE_SID_TXS(s->id, tx, "inspect_flags %x, total_matches %u, engine %p", |
1196 | 282k | inspect_flags, total_matches, engine); |
1197 | | |
1198 | 282k | if (engine == NULL && total_matches) { |
1199 | 73.4k | inspect_flags |= DE_STATE_FLAG_FULL_INSPECT; |
1200 | 73.4k | TRACE_SID_TXS(s->id, tx, "MATCH"); |
1201 | 73.4k | retval = true; |
1202 | 73.4k | } |
1203 | | |
1204 | 282k | if (stored_flags) { |
1205 | 37.5k | *stored_flags = inspect_flags; |
1206 | 37.5k | TRACE_SID_TXS(s->id, tx, "continue inspect flags %08x", inspect_flags); |
1207 | 244k | } else { |
1208 | | // store... or? If tx is done we might not want to come back to this tx |
1209 | | |
1210 | | // also... if mpmid tracking is enabled, we won't do a sig again for this tx... |
1211 | 244k | TRACE_SID_TXS(s->id, tx, "start inspect flags %08x", inspect_flags); |
1212 | 244k | if (inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) { |
1213 | 85.6k | if (file_no_match) { |
1214 | | /* if we have a mismatch on a file sig, we need to keep state. |
1215 | | * We may get another file on the same tx (for http and smtp |
1216 | | * at least), so for a new file we need to re-eval the sig. |
1217 | | * Thoughts / TODO: |
1218 | | * - not for some protos that have 1 file per tx (e.g. nfs) |
1219 | | * - maybe we only need this for file sigs that mix with |
1220 | | * other matches? E.g. 'POST + filename', is different than |
1221 | | * just 'filename'. |
1222 | | */ |
1223 | 10.8k | DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s, |
1224 | 10.8k | inspect_flags, flow_flags, file_no_match); |
1225 | 10.8k | } |
1226 | 159k | } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) && mpm_before_progress) { |
1227 | 45.3k | TRACE_SID_TXS(s->id, tx, "no need to store match sig, " |
1228 | 45.3k | "mpm won't trigger for it anymore"); |
1229 | | |
1230 | 45.3k | if (inspect_flags & DE_STATE_FLAG_FILE_INSPECT) { |
1231 | 3.61k | TRACE_SID_TXS(s->id, tx, "except that for new files, " |
1232 | 3.61k | "we may have to revisit anyway"); |
1233 | 3.61k | DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s, |
1234 | 3.61k | inspect_flags, flow_flags, file_no_match); |
1235 | 3.61k | } |
1236 | 113k | } else if ((inspect_flags & DE_STATE_FLAG_FULL_INSPECT) == 0 && mpm_in_progress) { |
1237 | 44.4k | TRACE_SID_TXS(s->id, tx, "no need to store no-match sig, " |
1238 | 44.4k | "mpm will revisit it"); |
1239 | 69.3k | } else { |
1240 | 69.3k | TRACE_SID_TXS(s->id, tx, "storing state: flags %08x", inspect_flags); |
1241 | 69.3k | DetectRunStoreStateTx(scratch->sgh, f, tx->tx_ptr, tx->tx_id, s, |
1242 | 69.3k | inspect_flags, flow_flags, file_no_match); |
1243 | 69.3k | } |
1244 | 244k | } |
1245 | | |
1246 | 282k | return retval; |
1247 | 282k | } |
1248 | | |
1249 | | #define NO_TX \ |
1250 | 28.0M | { \ |
1251 | 28.0M | NULL, 0, NULL, NULL, 0, 0, 0, 0, 0, \ |
1252 | 28.0M | } |
1253 | | |
1254 | | /** \internal |
1255 | | * \brief get a DetectTransaction object |
1256 | | * \retval struct filled with relevant info or all nulls/0s |
1257 | | */ |
1258 | | static DetectTransaction GetDetectTx(const uint8_t ipproto, const AppProto alproto, |
1259 | | void *alstate, const uint64_t tx_id, void *tx_ptr, const int tx_end_state, |
1260 | | const uint8_t flow_flags) |
1261 | 28.7M | { |
1262 | 28.7M | AppLayerTxData *txd = AppLayerParserGetTxData(ipproto, alproto, tx_ptr); |
1263 | 28.7M | if (unlikely(txd == NULL)) { |
1264 | 0 | DetectTransaction no_tx = NO_TX; |
1265 | 0 | return no_tx; |
1266 | 0 | } |
1267 | 28.7M | const int tx_progress = AppLayerParserGetStateProgress(ipproto, alproto, tx_ptr, flow_flags); |
1268 | 28.7M | bool updated = (flow_flags & STREAM_TOSERVER) ? txd->updated_ts : txd->updated_tc; |
1269 | 28.7M | if (!updated && tx_progress < tx_end_state && ((flow_flags & STREAM_EOF) == 0)) { |
1270 | 26.3M | DetectTransaction no_tx = NO_TX; |
1271 | 26.3M | return no_tx; |
1272 | 26.3M | } |
1273 | 2.35M | uint64_t detect_flags = |
1274 | 2.35M | (flow_flags & STREAM_TOSERVER) ? txd->detect_flags_ts : txd->detect_flags_tc; |
1275 | 2.35M | if (detect_flags & APP_LAYER_TX_INSPECTED_FLAG) { |
1276 | 1.64M | SCLogDebug("%"PRIu64" tx already fully inspected for %s. Flags %016"PRIx64, |
1277 | 1.64M | tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", |
1278 | 1.64M | detect_flags); |
1279 | 1.64M | DetectTransaction no_tx = NO_TX; |
1280 | 1.64M | return no_tx; |
1281 | 1.64M | } |
1282 | 716k | if (detect_flags & APP_LAYER_TX_SKIP_INSPECT_FLAG) { |
1283 | 34.7k | SCLogDebug("%" PRIu64 " tx should not be inspected in direction %s. Flags %016" PRIx64, |
1284 | 34.7k | tx_id, flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", detect_flags); |
1285 | 34.7k | DetectTransaction no_tx = NO_TX; |
1286 | 34.7k | return no_tx; |
1287 | 34.7k | } |
1288 | | |
1289 | 681k | const int dir_int = (flow_flags & STREAM_TOSERVER) ? 0 : 1; |
1290 | 681k | DetectEngineState *tx_de_state = txd->de_state; |
1291 | 681k | DetectEngineStateDirection *tx_dir_state = tx_de_state ? &tx_de_state->dir_state[dir_int] : NULL; |
1292 | 681k | uint64_t prefilter_flags = detect_flags & APP_LAYER_TX_PREFILTER_MASK; |
1293 | 681k | DEBUG_VALIDATE_BUG_ON(prefilter_flags & APP_LAYER_TX_RESERVED_FLAGS); |
1294 | | |
1295 | 681k | DetectTransaction tx = { |
1296 | 681k | .tx_ptr = tx_ptr, |
1297 | 681k | .tx_id = tx_id, |
1298 | 681k | .tx_data_ptr = (struct AppLayerTxData *)txd, |
1299 | 681k | .de_state = tx_dir_state, |
1300 | 681k | .detect_flags = detect_flags, |
1301 | 681k | .prefilter_flags = prefilter_flags, |
1302 | 681k | .prefilter_flags_orig = prefilter_flags, |
1303 | 681k | .tx_progress = tx_progress, |
1304 | 681k | .tx_end_state = tx_end_state, |
1305 | 681k | }; |
1306 | 681k | return tx; |
1307 | 681k | } |
1308 | | |
1309 | | static inline void StoreDetectFlags(DetectTransaction *tx, const uint8_t flow_flags, |
1310 | | const uint8_t ipproto, const AppProto alproto, const uint64_t detect_flags) |
1311 | 344k | { |
1312 | 344k | AppLayerTxData *txd = (AppLayerTxData *)tx->tx_data_ptr; |
1313 | 344k | if (likely(txd != NULL)) { |
1314 | 344k | if (flow_flags & STREAM_TOSERVER) { |
1315 | 215k | txd->detect_flags_ts = detect_flags; |
1316 | 215k | } else { |
1317 | 128k | txd->detect_flags_tc = detect_flags; |
1318 | 128k | } |
1319 | 344k | } |
1320 | 344k | } |
1321 | | |
1322 | | // Merge 'state' rules from the regular prefilter |
1323 | | // updates array_idx on the way |
1324 | | static inline void RuleMatchCandidateMergeStateRules( |
1325 | | DetectEngineThreadCtx *det_ctx, uint32_t *array_idx) |
1326 | 1.44M | { |
1327 | | // Now, we will merge 2 sorted lists : |
1328 | | // the one in det_ctx->tx_candidates |
1329 | | // and the one in det_ctx->match_array |
1330 | | // For match_array, we take only the relevant elements where s->app_inspect != NULL |
1331 | | |
1332 | | // Basically, we iterate at the same time over the 2 lists |
1333 | | // comparing and taking an element from either. |
1334 | | |
1335 | | // Trick is to do so in place in det_ctx->tx_candidates, |
1336 | | // so as to minimize the number of moves in det_ctx->tx_candidates. |
1337 | | // For this, the algorithm traverses the lists in reverse order. |
1338 | | // Otherwise, if the first element of match_array was to be put before |
1339 | | // all tx_candidates, we would need to shift all tx_candidates |
1340 | | |
1341 | | // Retain the number of elements sorted in tx_candidates before merge |
1342 | 1.44M | uint32_t j = *array_idx; |
1343 | | // First loop only counting the number of elements to add |
1344 | 2.29M | for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) { |
1345 | 850k | const Signature *s = det_ctx->match_array[i]; |
1346 | 850k | if (s->app_inspect != NULL) { |
1347 | 315k | (*array_idx)++; |
1348 | 315k | } |
1349 | 850k | } |
1350 | | // Future number of elements in tx_candidates after merge |
1351 | 1.44M | uint32_t k = *array_idx; |
1352 | | |
1353 | 1.44M | if (k == j) { |
1354 | | // no new element from match_array to merge in tx_candidates |
1355 | 1.15M | return; |
1356 | 1.15M | } |
1357 | | |
1358 | | // variable i is for all elements of match_array (even not relevant ones) |
1359 | | // variable j is for elements of tx_candidates before merge |
1360 | | // variable k is for elements of tx_candidates after merge |
1361 | 645k | for (uint32_t i = det_ctx->match_array_cnt; i > 0;) { |
1362 | 361k | const Signature *s = det_ctx->match_array[i - 1]; |
1363 | 361k | if (s->app_inspect == NULL) { |
1364 | | // no relevant element, get the next one from match_array |
1365 | 43.9k | i--; |
1366 | 43.9k | continue; |
1367 | 43.9k | } |
1368 | | // we have one element from match_array to merge in tx_candidates |
1369 | 317k | k--; |
1370 | 317k | if (j > 0) { |
1371 | | // j > 0 means there is still at least one element in tx_candidates to merge |
1372 | 2.88k | const RuleMatchCandidateTx *s0 = &det_ctx->tx_candidates[j - 1]; |
1373 | 2.88k | if (s->num <= s0->id) { |
1374 | | // get next element from previous tx_candidates |
1375 | 2.13k | j--; |
1376 | | // take the element from tx_candidates before merge |
1377 | 2.13k | det_ctx->tx_candidates[k].s = det_ctx->tx_candidates[j].s; |
1378 | 2.13k | det_ctx->tx_candidates[k].id = det_ctx->tx_candidates[j].id; |
1379 | 2.13k | det_ctx->tx_candidates[k].flags = det_ctx->tx_candidates[j].flags; |
1380 | 2.13k | det_ctx->tx_candidates[k].stream_reset = det_ctx->tx_candidates[j].stream_reset; |
1381 | 2.13k | continue; |
1382 | 2.13k | } |
1383 | 2.88k | } // otherwise |
1384 | | // get next element from match_array |
1385 | 315k | i--; |
1386 | | // take the element from match_array |
1387 | 315k | det_ctx->tx_candidates[k].s = s; |
1388 | 315k | det_ctx->tx_candidates[k].id = s->num; |
1389 | 315k | det_ctx->tx_candidates[k].flags = NULL; |
1390 | 315k | det_ctx->tx_candidates[k].stream_reset = 0; |
1391 | 315k | } |
1392 | | // Even if k > 0 or j > 0, the loop is over. (Note that j == k now) |
1393 | | // The remaining elements in tx_candidates up to k were already sorted |
1394 | | // and come before any other element later in the list |
1395 | 283k | } |
1396 | | |
1397 | | static void DetectRunTx(ThreadVars *tv, |
1398 | | DetectEngineCtx *de_ctx, |
1399 | | DetectEngineThreadCtx *det_ctx, |
1400 | | Packet *p, |
1401 | | Flow *f, |
1402 | | DetectRunScratchpad *scratch) |
1403 | 813k | { |
1404 | 813k | const uint8_t flow_flags = scratch->flow_flags; |
1405 | 813k | const SigGroupHead * const sgh = scratch->sgh; |
1406 | 813k | void * const alstate = f->alstate; |
1407 | 813k | const uint8_t ipproto = f->proto; |
1408 | 813k | const AppProto alproto = f->alproto; |
1409 | | |
1410 | 813k | const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate); |
1411 | 813k | uint64_t tx_id_min = AppLayerParserGetTransactionInspectId(f->alparser, flow_flags); |
1412 | 813k | const int tx_end_state = AppLayerParserGetStateProgressCompletionStatus(alproto, flow_flags); |
1413 | | |
1414 | 813k | AppLayerGetTxIteratorFunc IterFunc = AppLayerGetTxIterator(ipproto, alproto); |
1415 | 813k | AppLayerGetTxIterState state; |
1416 | 813k | memset(&state, 0, sizeof(state)); |
1417 | | |
1418 | 28.8M | while (1) { |
1419 | 28.8M | AppLayerGetTxIterTuple ires = IterFunc(ipproto, alproto, alstate, tx_id_min, total_txs, &state); |
1420 | 28.8M | if (ires.tx_ptr == NULL) |
1421 | 97.5k | break; |
1422 | | |
1423 | 28.7M | DetectTransaction tx = GetDetectTx(ipproto, alproto, |
1424 | 28.7M | alstate, ires.tx_id, ires.tx_ptr, tx_end_state, flow_flags); |
1425 | 28.7M | if (tx.tx_ptr == NULL) { |
1426 | 28.0M | SCLogDebug("%p/%"PRIu64" no transaction to inspect", |
1427 | 28.0M | tx.tx_ptr, tx_id_min); |
1428 | | |
1429 | 28.0M | tx_id_min++; // next (if any) run look for +1 |
1430 | 28.0M | goto next; |
1431 | 28.0M | } |
1432 | 681k | tx_id_min = tx.tx_id + 1; // next look for cur + 1 |
1433 | | |
1434 | 681k | bool do_sort = false; // do we need to sort the tx candidate list? |
1435 | 681k | uint32_t array_idx = 0; |
1436 | 681k | uint32_t total_rules = det_ctx->match_array_cnt; |
1437 | 681k | total_rules += (tx.de_state ? tx.de_state->cnt : 0); |
1438 | | |
1439 | | /* run prefilter engines and merge results into a candidates array */ |
1440 | 681k | if (sgh->tx_engines) { |
1441 | 189k | PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX); |
1442 | 189k | DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto, |
1443 | 189k | alstate, &tx); |
1444 | 189k | PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX); |
1445 | 189k | SCLogDebug("%p/%"PRIu64" rules added from prefilter: %u candidates", |
1446 | 189k | tx.tx_ptr, tx.tx_id, det_ctx->pmq.rule_id_array_cnt); |
1447 | | |
1448 | 189k | total_rules += det_ctx->pmq.rule_id_array_cnt; |
1449 | 189k | if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) { |
1450 | 485 | RuleMatchCandidateTxArrayExpand(det_ctx, total_rules); |
1451 | 485 | } |
1452 | | |
1453 | 194k | for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) { |
1454 | 5.13k | const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]]; |
1455 | 5.13k | const SigIntId id = s->num; |
1456 | 5.13k | det_ctx->tx_candidates[array_idx].s = s; |
1457 | 5.13k | det_ctx->tx_candidates[array_idx].id = id; |
1458 | 5.13k | det_ctx->tx_candidates[array_idx].flags = NULL; |
1459 | 5.13k | det_ctx->tx_candidates[array_idx].stream_reset = 0; |
1460 | 5.13k | array_idx++; |
1461 | 5.13k | } |
1462 | 189k | PMQ_RESET(&det_ctx->pmq); |
1463 | 492k | } else { |
1464 | 492k | if (!(RuleMatchCandidateTxArrayHasSpace(det_ctx, total_rules))) { |
1465 | 565 | RuleMatchCandidateTxArrayExpand(det_ctx, total_rules); |
1466 | 565 | } |
1467 | 492k | } |
1468 | | |
1469 | | /* merge 'state' rules from the regular prefilter */ |
1470 | | #ifdef PROFILING |
1471 | | uint32_t x = array_idx; |
1472 | | #endif |
1473 | 681k | RuleMatchCandidateMergeStateRules(det_ctx, &array_idx); |
1474 | | |
1475 | | /* merge stored state into results */ |
1476 | 681k | if (tx.de_state != NULL) { |
1477 | 85.0k | const uint32_t old = array_idx; |
1478 | | |
1479 | | /* if tx.de_state->flags has 'new file' set and sig below has |
1480 | | * 'file inspected' flag, reset the file part of the state */ |
1481 | 85.0k | const bool have_new_file = (tx.de_state->flags & DETECT_ENGINE_STATE_FLAG_FILE_NEW); |
1482 | 85.0k | if (have_new_file) { |
1483 | 4.76k | SCLogDebug("%p/%"PRIu64" destate: need to consider new file", |
1484 | 4.76k | tx.tx_ptr, tx.tx_id); |
1485 | 4.76k | tx.de_state->flags &= ~DETECT_ENGINE_STATE_FLAG_FILE_NEW; |
1486 | 4.76k | } |
1487 | | |
1488 | 85.0k | SigIntId state_cnt = 0; |
1489 | 85.0k | DeStateStore *tx_store = tx.de_state->head; |
1490 | 144k | for (; tx_store != NULL; tx_store = tx_store->next) { |
1491 | 59.1k | SCLogDebug("tx_store %p", tx_store); |
1492 | | |
1493 | 59.1k | SigIntId store_cnt = 0; |
1494 | 59.1k | for (store_cnt = 0; |
1495 | 120k | store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx.de_state->cnt; |
1496 | 61.3k | store_cnt++, state_cnt++) |
1497 | 61.3k | { |
1498 | 61.3k | DeStateStoreItem *item = &tx_store->store[store_cnt]; |
1499 | 61.3k | SCLogDebug("rule id %u, inspect_flags %u", item->sid, item->flags); |
1500 | 61.3k | if (have_new_file && (item->flags & DE_STATE_FLAG_FILE_INSPECT)) { |
1501 | | /* remove part of the state. File inspect engine will now |
1502 | | * be able to run again */ |
1503 | 135 | item->flags &= ~(DE_STATE_FLAG_SIG_CANT_MATCH|DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_FILE_INSPECT); |
1504 | 135 | SCLogDebug("rule id %u, post file reset inspect_flags %u", item->sid, item->flags); |
1505 | 135 | } |
1506 | 61.3k | det_ctx->tx_candidates[array_idx].s = de_ctx->sig_array[item->sid]; |
1507 | 61.3k | det_ctx->tx_candidates[array_idx].id = item->sid; |
1508 | 61.3k | det_ctx->tx_candidates[array_idx].flags = &item->flags; |
1509 | 61.3k | det_ctx->tx_candidates[array_idx].stream_reset = 0; |
1510 | 61.3k | array_idx++; |
1511 | 61.3k | } |
1512 | 59.1k | } |
1513 | 85.0k | do_sort |= (old && old != array_idx); // sort if continue list adds sids |
1514 | 85.0k | SCLogDebug("%p/%" PRIu64 " rules added from 'continue' list: %u", tx.tx_ptr, tx.tx_id, |
1515 | 85.0k | array_idx - old); |
1516 | 85.0k | } |
1517 | 681k | if (do_sort) { |
1518 | 56.9k | qsort(det_ctx->tx_candidates, array_idx, sizeof(RuleMatchCandidateTx), |
1519 | 56.9k | DetectRunTxSortHelper); |
1520 | 56.9k | } |
1521 | | |
1522 | | #ifdef PROFILING |
1523 | | if (array_idx >= de_ctx->profile_match_logging_threshold) |
1524 | | RulesDumpTxMatchArray(det_ctx, scratch->sgh, p, tx.tx_id, array_idx, x); |
1525 | | #endif |
1526 | 681k | det_ctx->tx_id = tx.tx_id; |
1527 | 681k | det_ctx->tx_id_set = true; |
1528 | 681k | det_ctx->p = p; |
1529 | | |
1530 | | #ifdef DEBUG |
1531 | | for (uint32_t i = 0; i < array_idx; i++) { |
1532 | | RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i]; |
1533 | | const Signature *s = det_ctx->tx_candidates[i].s; |
1534 | | SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags); |
1535 | | } |
1536 | | #endif |
1537 | | /* run rules: inspect the match candidates */ |
1538 | 993k | for (uint32_t i = 0; i < array_idx; i++) { |
1539 | 312k | RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i]; |
1540 | 312k | const Signature *s = det_ctx->tx_candidates[i].s; |
1541 | 312k | uint32_t *inspect_flags = det_ctx->tx_candidates[i].flags; |
1542 | | |
1543 | | /* deduplicate: rules_array is sorted, but not deduplicated: |
1544 | | * both mpm and stored state could give us the same sid. |
1545 | | * As they are back to back in that case we can check for it |
1546 | | * here. We select the stored state one as that comes first |
1547 | | * in the array. */ |
1548 | 372k | while ((i + 1) < array_idx && |
1549 | 89.4k | det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) { |
1550 | 60.0k | SCLogDebug("%p/%" PRIu64 " inspecting SKIP NEXT: sid %u (%u), flags %08x", |
1551 | 60.0k | tx.tx_ptr, tx.tx_id, s->id, s->num, inspect_flags ? *inspect_flags : 0); |
1552 | 60.0k | i++; |
1553 | 60.0k | } |
1554 | | |
1555 | 312k | SCLogDebug("%p/%"PRIu64" inspecting: sid %u (%u), flags %08x", |
1556 | 312k | tx.tx_ptr, tx.tx_id, s->id, s->num, inspect_flags ? *inspect_flags : 0); |
1557 | | |
1558 | 312k | if (inspect_flags) { |
1559 | 61.3k | if (*inspect_flags & (DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_SIG_CANT_MATCH)) { |
1560 | 23.7k | SCLogDebug("%p/%"PRIu64" inspecting: sid %u (%u), flags %08x ALREADY COMPLETE", |
1561 | 23.7k | tx.tx_ptr, tx.tx_id, s->id, s->num, *inspect_flags); |
1562 | 23.7k | continue; |
1563 | 23.7k | } |
1564 | 61.3k | } |
1565 | | |
1566 | 288k | if (inspect_flags) { |
1567 | | /* continue previous inspection */ |
1568 | 37.5k | SCLogDebug("%p/%" PRIu64 " Continuing sid %u", tx.tx_ptr, tx.tx_id, s->id); |
1569 | 250k | } else { |
1570 | | /* start new inspection */ |
1571 | 250k | SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id); |
1572 | 250k | } |
1573 | | |
1574 | | /* call individual rule inspection */ |
1575 | 288k | RULE_PROFILING_START(p); |
1576 | 288k | const int r = DetectRunTxInspectRule(tv, de_ctx, det_ctx, p, f, flow_flags, |
1577 | 288k | alstate, &tx, s, inspect_flags, can, scratch); |
1578 | 288k | if (r == 1) { |
1579 | | /* match */ |
1580 | 73.4k | DetectRunPostMatch(tv, det_ctx, p, s); |
1581 | | |
1582 | 73.4k | const uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_TX); |
1583 | 73.4k | SCLogDebug("%p/%"PRIu64" sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->num); |
1584 | 73.4k | AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags); |
1585 | 73.4k | } |
1586 | 288k | DetectVarProcessList(det_ctx, p->flow, p); |
1587 | 288k | RULE_PROFILING_END(det_ctx, s, r, p); |
1588 | 288k | } |
1589 | | |
1590 | 681k | det_ctx->tx_id = 0; |
1591 | 681k | det_ctx->tx_id_set = false; |
1592 | 681k | det_ctx->p = NULL; |
1593 | | |
1594 | | /* see if we have any updated state to store in the tx */ |
1595 | | |
1596 | 681k | uint64_t new_detect_flags = 0; |
1597 | | /* this side of the tx is done */ |
1598 | 681k | if (tx.tx_progress >= tx.tx_end_state) { |
1599 | 341k | new_detect_flags |= APP_LAYER_TX_INSPECTED_FLAG; |
1600 | 341k | SCLogDebug("%p/%"PRIu64" tx is done for direction %s. Flag %016"PRIx64, |
1601 | 341k | tx.tx_ptr, tx.tx_id, |
1602 | 341k | flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", |
1603 | 341k | new_detect_flags); |
1604 | 341k | } |
1605 | 681k | if (tx.prefilter_flags != tx.prefilter_flags_orig) { |
1606 | 50.9k | new_detect_flags |= tx.prefilter_flags; |
1607 | 50.9k | DEBUG_VALIDATE_BUG_ON(new_detect_flags & APP_LAYER_TX_RESERVED_FLAGS); |
1608 | 50.9k | SCLogDebug("%p/%"PRIu64" updated prefilter flags %016"PRIx64" " |
1609 | 50.9k | "(was: %016"PRIx64") for direction %s. Flag %016"PRIx64, |
1610 | 50.9k | tx.tx_ptr, tx.tx_id, tx.prefilter_flags, tx.prefilter_flags_orig, |
1611 | 50.9k | flow_flags & STREAM_TOSERVER ? "toserver" : "toclient", |
1612 | 50.9k | new_detect_flags); |
1613 | 50.9k | } |
1614 | 681k | if (new_detect_flags != 0 && |
1615 | 344k | (new_detect_flags | tx.detect_flags) != tx.detect_flags) |
1616 | 344k | { |
1617 | 344k | new_detect_flags |= tx.detect_flags; |
1618 | 344k | DEBUG_VALIDATE_BUG_ON(new_detect_flags & APP_LAYER_TX_RESERVED_FLAGS); |
1619 | 344k | SCLogDebug("%p/%"PRIu64" Storing new flags %016"PRIx64" (was %016"PRIx64")", |
1620 | 344k | tx.tx_ptr, tx.tx_id, new_detect_flags, tx.detect_flags); |
1621 | | |
1622 | 344k | StoreDetectFlags(&tx, flow_flags, ipproto, alproto, new_detect_flags); |
1623 | 344k | } |
1624 | 28.7M | next: |
1625 | 28.7M | InspectionBufferClean(det_ctx); |
1626 | | |
1627 | 28.7M | if (!ires.has_next) |
1628 | 716k | break; |
1629 | 28.7M | } |
1630 | 813k | } |
1631 | | |
1632 | | static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
1633 | | Packet *p, Flow *f, DetectRunScratchpad *scratch) |
1634 | 1.55M | { |
1635 | 1.55M | const SigGroupHead *const sgh = scratch->sgh; |
1636 | 1.55M | const AppProto alproto = f->alproto; |
1637 | | |
1638 | 1.55M | FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
1639 | 1.55M | if (frames_container == NULL) { |
1640 | 363k | return; |
1641 | 363k | } |
1642 | 1.18M | Frames *frames; |
1643 | 1.18M | if (PKT_IS_TOSERVER(p)) { |
1644 | 696k | frames = &frames_container->toserver; |
1645 | 696k | } else { |
1646 | 490k | frames = &frames_container->toclient; |
1647 | 490k | } |
1648 | | |
1649 | 4.95M | for (uint32_t idx = 0; idx < frames->cnt; idx++) { |
1650 | 3.77M | SCLogDebug("frame %u", idx); |
1651 | 3.77M | Frame *frame = FrameGetByIndex(frames, idx); |
1652 | 3.77M | if (frame == NULL) { |
1653 | 0 | continue; |
1654 | 0 | } |
1655 | | |
1656 | 3.77M | det_ctx->frame_inspect_progress = 0; |
1657 | 3.77M | uint32_t array_idx = 0; |
1658 | 3.77M | uint32_t total_rules = det_ctx->match_array_cnt; |
1659 | | |
1660 | | /* run prefilter engines and merge results into a candidates array */ |
1661 | 3.77M | if (sgh->frame_engines) { |
1662 | | // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX); |
1663 | 267k | DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto); |
1664 | | // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX); |
1665 | 267k | SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id, |
1666 | 267k | det_ctx->pmq.rule_id_array_cnt); |
1667 | | |
1668 | 267k | total_rules += det_ctx->pmq.rule_id_array_cnt; |
1669 | | |
1670 | 267k | if (!(RuleMatchCandidateTxArrayHasSpace( |
1671 | 267k | det_ctx, total_rules))) { // TODO is it safe to overload? |
1672 | 10 | RuleMatchCandidateTxArrayExpand(det_ctx, total_rules); |
1673 | 10 | } |
1674 | | |
1675 | 268k | for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) { |
1676 | 595 | const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]]; |
1677 | 595 | const SigIntId id = s->num; |
1678 | 595 | det_ctx->tx_candidates[array_idx].s = s; |
1679 | 595 | det_ctx->tx_candidates[array_idx].id = id; |
1680 | 595 | det_ctx->tx_candidates[array_idx].flags = NULL; |
1681 | 595 | det_ctx->tx_candidates[array_idx].stream_reset = 0; |
1682 | 595 | array_idx++; |
1683 | 595 | } |
1684 | 267k | PMQ_RESET(&det_ctx->pmq); |
1685 | 267k | } |
1686 | | /* merge 'state' rules from the regular prefilter */ |
1687 | 3.77M | uint32_t x = array_idx; |
1688 | 6.97M | for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) { |
1689 | 3.20M | const Signature *s = det_ctx->match_array[i]; |
1690 | 3.20M | if (s->frame_inspect != NULL) { |
1691 | 110k | const SigIntId id = s->num; |
1692 | 110k | det_ctx->tx_candidates[array_idx].s = s; |
1693 | 110k | det_ctx->tx_candidates[array_idx].id = id; |
1694 | 110k | det_ctx->tx_candidates[array_idx].flags = NULL; |
1695 | 110k | det_ctx->tx_candidates[array_idx].stream_reset = 0; |
1696 | 110k | array_idx++; |
1697 | | |
1698 | 110k | SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id, |
1699 | 110k | s->id, id); |
1700 | 110k | } |
1701 | 3.20M | } |
1702 | 3.77M | SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id, |
1703 | 3.77M | array_idx - x); |
1704 | 3.77M | (void)x; |
1705 | | |
1706 | | /* run rules: inspect the match candidates */ |
1707 | 3.88M | for (uint32_t i = 0; i < array_idx; i++) { |
1708 | 111k | const Signature *s = det_ctx->tx_candidates[i].s; |
1709 | | |
1710 | | /* deduplicate: rules_array is sorted, but not deduplicated. |
1711 | | * As they are back to back in that case we can check for it |
1712 | | * here. We select the stored state one as that comes first |
1713 | | * in the array. */ |
1714 | 111k | while ((i + 1) < array_idx && |
1715 | 212 | det_ctx->tx_candidates[i].s == det_ctx->tx_candidates[i + 1].s) { |
1716 | 48 | i++; |
1717 | 48 | } |
1718 | 111k | SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->num); |
1719 | | |
1720 | | /* start new inspection */ |
1721 | 111k | SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id); |
1722 | | |
1723 | | /* call individual rule inspection */ |
1724 | 111k | RULE_PROFILING_START(p); |
1725 | 111k | int r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags); |
1726 | 111k | if (r == 1) { |
1727 | 111k | r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame); |
1728 | 111k | if (r == 1) { |
1729 | | /* match */ |
1730 | 12.3k | DetectRunPostMatch(tv, det_ctx, p, s); |
1731 | | |
1732 | 12.3k | uint8_t alert_flags = (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME); |
1733 | 12.3k | det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET; |
1734 | 12.3k | det_ctx->frame_id = frame->id; |
1735 | 12.3k | SCLogDebug( |
1736 | 12.3k | "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->num); |
1737 | 12.3k | if (frame->flags & FRAME_FLAG_TX_ID_SET) { |
1738 | 11.6k | alert_flags |= PACKET_ALERT_FLAG_TX; |
1739 | 11.6k | } |
1740 | 12.3k | AlertQueueAppend(det_ctx, s, p, frame->tx_id, alert_flags); |
1741 | 12.3k | } |
1742 | 111k | } |
1743 | 111k | DetectVarProcessList(det_ctx, p->flow, p); |
1744 | 111k | RULE_PROFILING_END(det_ctx, s, r, p); |
1745 | 111k | } |
1746 | | |
1747 | | /* update Frame::inspect_progress here instead of in the code above. The reason is that a |
1748 | | * frame might be used more than once in buffers with transforms. */ |
1749 | 3.77M | if (frame->inspect_progress < det_ctx->frame_inspect_progress) { |
1750 | 17.0k | frame->inspect_progress = det_ctx->frame_inspect_progress; |
1751 | 17.0k | SCLogDebug("frame->inspect_progress: %" PRIu64 " -> updated", frame->inspect_progress); |
1752 | 3.75M | } else { |
1753 | 3.75M | SCLogDebug( |
1754 | 3.75M | "frame->inspect_progress: %" PRIu64 " -> not updated", frame->inspect_progress); |
1755 | 3.75M | } |
1756 | | |
1757 | 3.77M | SCLogDebug("%p/%" PRIi64 " rules inspected, running cleanup", frame, frame->id); |
1758 | 3.77M | InspectionBufferClean(det_ctx); |
1759 | 3.77M | } |
1760 | 1.18M | } |
1761 | | |
1762 | | static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id) |
1763 | 0 | { |
1764 | | /* technically we need to pass a DetectEngineThreadCtx struct with the |
1765 | | * tenant_id member. But as that member is the first in the struct, we |
1766 | | * can use the id directly. */ |
1767 | 0 | return HashTableLookup(h, &id, 0); |
1768 | 0 | } |
1769 | | |
1770 | | static void DetectFlow(ThreadVars *tv, |
1771 | | DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
1772 | | Packet *p) |
1773 | 7.15M | { |
1774 | 7.15M | Flow *const f = p->flow; |
1775 | | |
1776 | 7.15M | if (p->flags & PKT_NOPACKET_INSPECTION) { |
1777 | | /* hack: if we are in pass the entire flow mode, we need to still |
1778 | | * update the inspect_id forward. So test for the condition here, |
1779 | | * and call the update code if necessary. */ |
1780 | 35.7k | const int pass = ((f->flags & FLOW_NOPACKET_INSPECTION)); |
1781 | 35.7k | if (pass) { |
1782 | 35.7k | uint8_t flags = STREAM_FLAGS_FOR_PACKET(p); |
1783 | 35.7k | flags = FlowGetDisruptionFlags(f, flags); |
1784 | 35.7k | if (f->alstate) { |
1785 | 31.2k | AppLayerParserSetTransactionInspectId(f, f->alparser, f->alstate, flags, true); |
1786 | 31.2k | } |
1787 | 35.7k | } |
1788 | 35.7k | SCLogDebug("p->pcap %"PRIu64": no detection on packet, " |
1789 | 35.7k | "PKT_NOPACKET_INSPECTION is set", p->pcap_cnt); |
1790 | 35.7k | return; |
1791 | 35.7k | } |
1792 | | |
1793 | | /* we check the flow drop here, and not the packet drop. This is |
1794 | | * to allow stream engine "invalid" drop packets to still be |
1795 | | * evaluated by the stream event rules. */ |
1796 | 7.12M | if (f->flags & FLOW_ACTION_DROP) { |
1797 | 2.68k | DEBUG_VALIDATE_BUG_ON(!(PKT_IS_PSEUDOPKT(p)) && !PacketCheckAction(p, ACTION_DROP)); |
1798 | 2.68k | SCReturn; |
1799 | 2.68k | } |
1800 | | |
1801 | | /* see if the packet matches one or more of the sigs */ |
1802 | 7.11M | (void)DetectRun(tv, de_ctx, det_ctx, p); |
1803 | 7.11M | } |
1804 | | |
1805 | | |
1806 | | static void DetectNoFlow(ThreadVars *tv, |
1807 | | DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, |
1808 | | Packet *p) |
1809 | 1.56M | { |
1810 | | /* No need to perform any detection on this packet, if the given flag is set.*/ |
1811 | 1.56M | if ((p->flags & PKT_NOPACKET_INSPECTION) || (PacketCheckAction(p, ACTION_DROP))) { |
1812 | 0 | return; |
1813 | 0 | } |
1814 | | |
1815 | | /* see if the packet matches one or more of the sigs */ |
1816 | 1.56M | DetectRun(tv, de_ctx, det_ctx, p); |
1817 | 1.56M | return; |
1818 | 1.56M | } |
1819 | | |
1820 | | /** \brief Detection engine thread wrapper. |
1821 | | * \param tv thread vars |
1822 | | * \param p packet to inspect |
1823 | | * \param data thread specific data |
1824 | | * \param pq packet queue |
1825 | | * \retval TM_ECODE_FAILED error |
1826 | | * \retval TM_ECODE_OK ok |
1827 | | */ |
1828 | | TmEcode Detect(ThreadVars *tv, Packet *p, void *data) |
1829 | 8.72M | { |
1830 | 8.72M | DEBUG_VALIDATE_PACKET(p); |
1831 | | |
1832 | 8.72M | DetectEngineCtx *de_ctx = NULL; |
1833 | 8.72M | DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data; |
1834 | 8.72M | if (det_ctx == NULL) { |
1835 | 0 | printf("ERROR: Detect has no thread ctx\n"); |
1836 | 0 | goto error; |
1837 | 0 | } |
1838 | | |
1839 | 8.72M | if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) { |
1840 | 44.5k | (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1); |
1841 | 44.5k | SCLogDebug("Detect Engine using new det_ctx - %p", |
1842 | 44.5k | det_ctx); |
1843 | 44.5k | } |
1844 | | |
1845 | | /* if in MT mode _and_ we have tenants registered, use |
1846 | | * MT logic. */ |
1847 | 8.72M | if (det_ctx->mt_det_ctxs_cnt > 0 && det_ctx->TenantGetId != NULL) |
1848 | 0 | { |
1849 | 0 | uint32_t tenant_id = p->tenant_id; |
1850 | 0 | if (tenant_id == 0) |
1851 | 0 | tenant_id = det_ctx->TenantGetId(det_ctx, p); |
1852 | 0 | if (tenant_id > 0 && tenant_id < det_ctx->mt_det_ctxs_cnt) { |
1853 | 0 | p->tenant_id = tenant_id; |
1854 | 0 | det_ctx = GetTenantById(det_ctx->mt_det_ctxs_hash, tenant_id); |
1855 | 0 | if (det_ctx == NULL) |
1856 | 0 | return TM_ECODE_OK; |
1857 | 0 | de_ctx = det_ctx->de_ctx; |
1858 | 0 | if (de_ctx == NULL) |
1859 | 0 | return TM_ECODE_OK; |
1860 | | |
1861 | 0 | if (unlikely(SC_ATOMIC_GET(det_ctx->so_far_used_by_detect) == 0)) { |
1862 | 0 | (void)SC_ATOMIC_SET(det_ctx->so_far_used_by_detect, 1); |
1863 | 0 | SCLogDebug("MT de_ctx %p det_ctx %p (tenant %u)", de_ctx, det_ctx, tenant_id); |
1864 | 0 | } |
1865 | 0 | } else { |
1866 | | /* use default if no tenants are registered for this packet */ |
1867 | 0 | de_ctx = det_ctx->de_ctx; |
1868 | 0 | } |
1869 | 8.72M | } else { |
1870 | 8.72M | de_ctx = det_ctx->de_ctx; |
1871 | 8.72M | } |
1872 | | |
1873 | 8.72M | if (p->flow) { |
1874 | 7.15M | DetectFlow(tv, de_ctx, det_ctx, p); |
1875 | 7.15M | } else { |
1876 | 1.56M | DetectNoFlow(tv, de_ctx, det_ctx, p); |
1877 | 1.56M | } |
1878 | | |
1879 | | #ifdef PROFILE_RULES |
1880 | | /* aggregate statistics */ |
1881 | | struct timeval ts; |
1882 | | gettimeofday(&ts, NULL); |
1883 | | if (ts.tv_sec != det_ctx->rule_perf_last_sync) { |
1884 | | SCProfilingRuleThreatAggregate(det_ctx); |
1885 | | det_ctx->rule_perf_last_sync = ts.tv_sec; |
1886 | | } |
1887 | | #endif |
1888 | | |
1889 | 8.72M | return TM_ECODE_OK; |
1890 | 0 | error: |
1891 | 0 | return TM_ECODE_FAILED; |
1892 | 8.72M | } |
1893 | | |
1894 | | /** \brief disable file features we don't need |
1895 | | * Called if we have no detection engine. |
1896 | | */ |
1897 | | void DisableDetectFlowFileFlags(Flow *f) |
1898 | 0 | { |
1899 | 0 | DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOSERVER); |
1900 | 0 | DetectPostInspectFileFlagsUpdate(f, NULL /* no sgh */, STREAM_TOCLIENT); |
1901 | 0 | } |
1902 | | |
1903 | | #ifdef UNITTESTS |
1904 | | /** |
1905 | | * \brief wrapper for old tests |
1906 | | */ |
1907 | | void SigMatchSignatures( |
1908 | | ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p) |
1909 | | { |
1910 | | if (p->flow) { |
1911 | | DetectFlow(tv, de_ctx, det_ctx, p); |
1912 | | } else { |
1913 | | DetectNoFlow(tv, de_ctx, det_ctx, p); |
1914 | | } |
1915 | | } |
1916 | | #endif |
1917 | | |
1918 | | /* |
1919 | | * TESTS |
1920 | | */ |
1921 | | |
1922 | | #ifdef UNITTESTS |
1923 | | #include "tests/detect.c" |
1924 | | #endif |
1925 | | |