/src/suricata7/src/app-layer-frames.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 | | */ |
24 | | |
25 | | #include "suricata-common.h" |
26 | | #include "util-print.h" |
27 | | |
28 | | #include "flow.h" |
29 | | #include "stream-tcp.h" |
30 | | #include "app-layer-frames.h" |
31 | | #include "app-layer-parser.h" |
32 | | |
33 | | struct FrameConfig { |
34 | | SC_ATOMIC_DECLARE(uint64_t, types); |
35 | | }; |
36 | | static struct FrameConfig frame_config[ALPROTO_MAX]; |
37 | | |
38 | | void FrameConfigInit(void) |
39 | 32 | { |
40 | 1.15k | for (AppProto p = 0; p < ALPROTO_MAX; p++) { |
41 | 1.12k | SC_ATOMIC_INIT(frame_config[p].types); |
42 | 1.12k | } |
43 | 32 | } |
44 | | |
45 | | void FrameConfigEnableAll(void) |
46 | 0 | { |
47 | 0 | const uint64_t bits = UINT64_MAX; |
48 | 0 | for (AppProto p = 0; p < ALPROTO_MAX; p++) { |
49 | 0 | struct FrameConfig *fc = &frame_config[p]; |
50 | 0 | SC_ATOMIC_OR(fc->types, bits); |
51 | 0 | } |
52 | 0 | } |
53 | | |
54 | | void FrameConfigEnable(const AppProto p, const uint8_t type) |
55 | 43.4k | { |
56 | 43.4k | const uint64_t bits = BIT_U64(type); |
57 | 43.4k | struct FrameConfig *fc = &frame_config[p]; |
58 | 43.4k | SC_ATOMIC_OR(fc->types, bits); |
59 | 43.4k | } |
60 | | |
61 | | static inline bool FrameConfigTypeIsEnabled(const AppProto p, const uint8_t type) |
62 | 411M | { |
63 | 411M | struct FrameConfig *fc = &frame_config[p]; |
64 | 411M | const uint64_t bits = BIT_U64(type); |
65 | 411M | const bool enabled = (SC_ATOMIC_GET(fc->types) & bits) != 0; |
66 | 411M | return enabled; |
67 | 411M | } |
68 | | |
69 | | static void FrameDebug(const char *prefix, const Frames *frames, const Frame *frame) |
70 | 40.6M | { |
71 | | #ifdef DEBUG |
72 | | const char *type_name = "unknown"; |
73 | | if (frame->type == FRAME_STREAM_TYPE) { |
74 | | type_name = "stream"; |
75 | | } else if (frames != NULL) { |
76 | | type_name = AppLayerParserGetFrameNameById(frames->ipproto, frames->alproto, frame->type); |
77 | | } |
78 | | SCLogDebug("[%s] %p: frame:%p type:%u/%s id:%" PRIi64 " flags:%02x offset:%" PRIu64 |
79 | | ", len:%" PRIi64 ", inspect_progress:%" PRIu64 ", events:%u %u/%u/%u/%u", |
80 | | prefix, frames, frame, frame->type, type_name, frame->id, frame->flags, frame->offset, |
81 | | frame->len, frame->inspect_progress, frame->event_cnt, frame->events[0], |
82 | | frame->events[1], frame->events[2], frame->events[3]); |
83 | | #endif |
84 | 40.6M | } |
85 | | |
86 | | Frame *FrameGetById(Frames *frames, const int64_t id) |
87 | 862k | { |
88 | 862k | SCLogDebug("frames %p cnt %u, looking for %" PRIi64, frames, frames->cnt, id); |
89 | 12.4M | for (uint16_t i = 0; i < frames->cnt; i++) { |
90 | 12.3M | if (i < FRAMES_STATIC_CNT) { |
91 | 1.73M | Frame *frame = &frames->sframes[i]; |
92 | 1.73M | FrameDebug("get_by_id(static)", frames, frame); |
93 | 1.73M | if (frame->id == id) |
94 | 548k | return frame; |
95 | 10.6M | } else { |
96 | 10.6M | const uint16_t o = i - FRAMES_STATIC_CNT; |
97 | 10.6M | Frame *frame = &frames->dframes[o]; |
98 | 10.6M | FrameDebug("get_by_id(dynamic)", frames, frame); |
99 | 10.6M | if (frame->id == id) |
100 | 258k | return frame; |
101 | 10.6M | } |
102 | 12.3M | } |
103 | 54.8k | return NULL; |
104 | 862k | } |
105 | | |
106 | | Frame *FrameGetByIndex(Frames *frames, const uint32_t idx) |
107 | 9.60M | { |
108 | 9.60M | if (idx >= frames->cnt) |
109 | 0 | return NULL; |
110 | | |
111 | 9.60M | if (idx < FRAMES_STATIC_CNT) { |
112 | 2.93M | Frame *frame = &frames->sframes[idx]; |
113 | 2.93M | FrameDebug("get_by_idx(s)", frames, frame); |
114 | 2.93M | return frame; |
115 | 6.66M | } else { |
116 | 6.66M | const uint32_t o = idx - FRAMES_STATIC_CNT; |
117 | 6.66M | Frame *frame = &frames->dframes[o]; |
118 | 6.66M | FrameDebug("get_by_idx(d)", frames, frame); |
119 | 6.66M | return frame; |
120 | 6.66M | } |
121 | 9.60M | } |
122 | | |
123 | | static Frame *FrameNew(Frames *frames, uint64_t offset, int64_t len) |
124 | 4.58M | { |
125 | 4.58M | BUG_ON(frames == NULL); |
126 | | |
127 | 4.58M | if (frames->cnt < FRAMES_STATIC_CNT) { |
128 | 2.15M | Frame *frame = &frames->sframes[frames->cnt]; |
129 | 2.15M | frames->sframes[frames->cnt].offset = offset; |
130 | 2.15M | frames->sframes[frames->cnt].len = len; |
131 | 2.15M | frames->sframes[frames->cnt].id = ++frames->base_id; |
132 | 2.15M | frames->cnt++; |
133 | 2.15M | return frame; |
134 | 2.42M | } else if (frames->dframes == NULL) { |
135 | 26.5k | BUG_ON(frames->dyn_size != 0); |
136 | 26.5k | BUG_ON(frames->cnt != FRAMES_STATIC_CNT); |
137 | | |
138 | 26.5k | frames->dframes = SCCalloc(8, sizeof(Frame)); |
139 | 26.5k | if (frames->dframes == NULL) { |
140 | 0 | return NULL; |
141 | 0 | } |
142 | 26.5k | frames->cnt++; |
143 | 26.5k | BUG_ON(frames->cnt != FRAMES_STATIC_CNT + 1); |
144 | | |
145 | 26.5k | frames->dyn_size = 8; |
146 | 26.5k | frames->dframes[0].offset = offset; |
147 | 26.5k | frames->dframes[0].len = len; |
148 | 26.5k | frames->dframes[0].id = ++frames->base_id; |
149 | 26.5k | return &frames->dframes[0]; |
150 | 2.39M | } else { |
151 | 2.39M | BUG_ON(frames->cnt < FRAMES_STATIC_CNT); |
152 | | |
153 | | /* need to handle dynamic storage of frames now */ |
154 | 2.39M | const uint16_t dyn_cnt = frames->cnt - FRAMES_STATIC_CNT; |
155 | 2.39M | if (dyn_cnt < frames->dyn_size) { |
156 | 1.60M | BUG_ON(frames->dframes == NULL); |
157 | | |
158 | | // fall through |
159 | 1.60M | } else { |
160 | 792k | if (frames->dyn_size == 256) { |
161 | 772k | SCLogDebug("limit reached! 256 dynamic frames already"); |
162 | | // limit reached |
163 | | // TODO figure out if this should lead to an event of sorts |
164 | 772k | return NULL; |
165 | 772k | } |
166 | | |
167 | | /* realloc time */ |
168 | 19.9k | uint16_t new_dyn_size = frames->dyn_size * 2; |
169 | 19.9k | uint32_t new_alloc_size = new_dyn_size * sizeof(Frame); |
170 | | |
171 | 19.9k | void *ptr = SCRealloc(frames->dframes, new_alloc_size); |
172 | 19.9k | if (ptr == NULL) { |
173 | 0 | return NULL; |
174 | 0 | } |
175 | | |
176 | 19.9k | memset((uint8_t *)ptr + (frames->dyn_size * sizeof(Frame)), 0x00, |
177 | 19.9k | (frames->dyn_size * sizeof(Frame))); |
178 | 19.9k | frames->dframes = ptr; |
179 | 19.9k | frames->dyn_size = new_dyn_size; |
180 | 19.9k | } |
181 | | |
182 | 1.62M | frames->cnt++; |
183 | 1.62M | frames->dframes[dyn_cnt].offset = offset; |
184 | 1.62M | frames->dframes[dyn_cnt].len = len; |
185 | 1.62M | frames->dframes[dyn_cnt].id = ++frames->base_id; |
186 | 1.62M | return &frames->dframes[dyn_cnt]; |
187 | 2.39M | } |
188 | 4.58M | } |
189 | | |
190 | | static void FrameClean(Frame *frame) |
191 | 9.29M | { |
192 | 9.29M | memset(frame, 0, sizeof(*frame)); |
193 | 9.29M | } |
194 | | |
195 | | static void FrameCopy(Frame *dst, Frame *src) |
196 | 15.0M | { |
197 | 15.0M | memcpy(dst, src, sizeof(*dst)); |
198 | 15.0M | } |
199 | | |
200 | | static void AppLayerFrameDumpForFrames(const char *prefix, const Frames *frames) |
201 | 2.40M | { |
202 | 2.40M | SCLogDebug("prefix: %s", prefix); |
203 | 21.2M | for (uint16_t i = 0; i < frames->cnt; i++) { |
204 | 18.8M | if (i < FRAMES_STATIC_CNT) { |
205 | 3.27M | const Frame *frame = &frames->sframes[i]; |
206 | 3.27M | FrameDebug(prefix, frames, frame); |
207 | 15.5M | } else { |
208 | 15.5M | const uint16_t o = i - FRAMES_STATIC_CNT; |
209 | 15.5M | const Frame *frame = &frames->dframes[o]; |
210 | 15.5M | FrameDebug(prefix, frames, frame); |
211 | 15.5M | } |
212 | 18.8M | } |
213 | 2.40M | SCLogDebug("prefix: %s", prefix); |
214 | 2.40M | } |
215 | | |
216 | | static inline uint64_t FrameLeftEdge(const TcpStream *stream, const Frame *frame) |
217 | 99.1M | { |
218 | 99.1M | const int64_t app_progress = STREAM_APP_PROGRESS(stream); |
219 | | |
220 | 99.1M | const int64_t frame_offset = frame->offset; |
221 | 99.1M | const int64_t frame_data = app_progress - frame_offset; |
222 | | |
223 | 99.1M | SCLogDebug("frame_offset %" PRIi64 ", frame_data %" PRIi64 ", frame->len %" PRIi64, |
224 | 99.1M | frame_offset, frame_data, frame->len); |
225 | 99.1M | BUG_ON(frame_offset > app_progress); |
226 | | |
227 | | /* length unknown, make sure to have at least 2500 */ |
228 | 99.1M | if (frame->len < 0) { |
229 | 6.59M | if (frame_data <= 2500) { |
230 | 1.30M | SCLogDebug("got <= 2500 bytes (%" PRIu64 "), returning offset %" PRIu64, frame_data, |
231 | 1.30M | frame_offset); |
232 | 1.30M | return frame_offset; |
233 | 5.28M | } else { |
234 | 5.28M | SCLogDebug("got > 2500 bytes (%" PRIu64 "), returning offset %" PRIu64, frame_data, |
235 | 5.28M | (frame_offset + (frame_data - 2500))); |
236 | 5.28M | return frame_offset + (frame_data - 2500); |
237 | 5.28M | } |
238 | | |
239 | | /* length specified */ |
240 | 92.5M | } else { |
241 | | /* have all data for the frame, we can skip it */ |
242 | 92.5M | if (frame->len <= frame_data) { |
243 | 33.3M | uint64_t x = frame_offset + frame_data; |
244 | 33.3M | SCLogDebug("x %" PRIu64, x); |
245 | 33.3M | return x; |
246 | | /* |
247 | | |
248 | | [ stream <frame_data> ] |
249 | | [ frame .......] |
250 | | |
251 | | */ |
252 | 59.2M | } else if (frame_data < 2500) { |
253 | 56.6M | uint64_t x = frame_offset; |
254 | 56.6M | SCLogDebug("x %" PRIu64, x); |
255 | 56.6M | return x; |
256 | 56.6M | } else { |
257 | 2.63M | uint64_t x = frame_offset + (frame_data - 2500); |
258 | 2.63M | SCLogDebug("x %" PRIu64, x); |
259 | 2.63M | return x; |
260 | 2.63M | } |
261 | 92.5M | } |
262 | 99.1M | } |
263 | | |
264 | | /** Stream buffer slides forward, we need to update and age out |
265 | | * frame offsets/frames. Aging out means we move existing frames |
266 | | * into the slots we'd free up. |
267 | | * |
268 | | * Start: |
269 | | * |
270 | | * [ stream ] |
271 | | * [ frame ...........] |
272 | | * offset: 2 |
273 | | * len: 19 |
274 | | * |
275 | | * Slide: |
276 | | * [ stream ] |
277 | | * [ frame .... .] |
278 | | * offset: 2 |
279 | | * len: 19 |
280 | | * |
281 | | * Slide: |
282 | | * [ stream ] |
283 | | * [ frame ........... ] |
284 | | * offset: 2 |
285 | | * len: 19 |
286 | | */ |
287 | | static int FrameSlide(const char *ds, Frames *frames, const TcpStream *stream, const uint32_t slide) |
288 | 699k | { |
289 | 699k | SCLogDebug("start: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64 |
290 | 699k | ", next %" PRIu64, |
291 | 699k | (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel, |
292 | 699k | STREAM_BASE_OFFSET(stream), STREAM_BASE_OFFSET(stream) + slide); |
293 | 699k | BUG_ON(frames == NULL); |
294 | 699k | SCLogDebug("%s frames %p: sliding %u bytes", ds, frames, slide); |
295 | 699k | uint64_t le = STREAM_APP_PROGRESS(stream); |
296 | 699k | const uint64_t next_base = STREAM_BASE_OFFSET(stream) + slide; |
297 | 699k | const uint16_t start = frames->cnt; |
298 | 699k | uint16_t removed = 0; |
299 | 699k | uint16_t x = 0; |
300 | 2.21M | for (uint16_t i = 0; i < frames->cnt; i++) { |
301 | 1.51M | if (i < FRAMES_STATIC_CNT) { |
302 | 715k | Frame *frame = &frames->sframes[i]; |
303 | 715k | FrameDebug("slide(s)", frames, frame); |
304 | 715k | if (frame->len >= 0 && frame->offset + frame->len <= next_base) { |
305 | | // remove by not incrementing 'x' |
306 | 15.6k | SCLogDebug("removing %p id %" PRIi64, frame, frame->id); |
307 | 15.6k | FrameClean(frame); |
308 | 15.6k | removed++; |
309 | 699k | } else { |
310 | 699k | Frame *nframe = &frames->sframes[x]; |
311 | 699k | FrameCopy(nframe, frame); |
312 | 699k | if (frame != nframe) { |
313 | 154 | FrameClean(frame); |
314 | 154 | } |
315 | 699k | le = MIN(le, FrameLeftEdge(stream, nframe)); |
316 | 699k | x++; |
317 | 699k | } |
318 | 795k | } else { |
319 | 795k | const uint16_t o = i - FRAMES_STATIC_CNT; |
320 | 795k | Frame *frame = &frames->dframes[o]; |
321 | 795k | FrameDebug("slide(d)", frames, frame); |
322 | 795k | if (frame->len >= 0 && frame->offset + frame->len <= next_base) { |
323 | | // remove by not incrementing 'x' |
324 | 5.57k | SCLogDebug("removing %p id %" PRIi64, frame, frame->id); |
325 | 5.57k | FrameClean(frame); |
326 | 5.57k | removed++; |
327 | 790k | } else { |
328 | 790k | Frame *nframe; |
329 | 790k | if (x >= FRAMES_STATIC_CNT) { |
330 | 789k | nframe = &frames->dframes[x - FRAMES_STATIC_CNT]; |
331 | 789k | } else { |
332 | 190 | nframe = &frames->sframes[x]; |
333 | 190 | } |
334 | 790k | FrameCopy(nframe, frame); |
335 | 790k | if (frame != nframe) { |
336 | 6.73k | FrameClean(frame); |
337 | 6.73k | } |
338 | 790k | le = MIN(le, FrameLeftEdge(stream, nframe)); |
339 | 790k | x++; |
340 | 790k | } |
341 | 795k | } |
342 | 1.51M | } |
343 | 699k | frames->cnt = x; |
344 | 699k | uint64_t o = STREAM_BASE_OFFSET(stream) + slide; |
345 | 699k | frames->left_edge_rel = le - (STREAM_BASE_OFFSET(stream) + slide); |
346 | | |
347 | | #ifdef DEBUG |
348 | | SCLogDebug("end: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64 |
349 | | " (+slide), cnt %u, removed %u, start %u", |
350 | | (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream) + slide, |
351 | | frames->left_edge_rel, STREAM_BASE_OFFSET(stream) + slide, frames->cnt, removed, start); |
352 | | char pf[32] = ""; |
353 | | snprintf(pf, sizeof(pf), "%s:post_slide", ds); |
354 | | AppLayerFrameDumpForFrames(pf, frames); |
355 | | #endif |
356 | 699k | BUG_ON(o > le); |
357 | 699k | BUG_ON(x != start - removed); |
358 | 699k | return 0; |
359 | 699k | } |
360 | | |
361 | | void AppLayerFramesSlide(Flow *f, const uint32_t slide, const uint8_t direction) |
362 | 1.24M | { |
363 | 1.24M | FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
364 | 1.24M | if (frames_container == NULL) |
365 | 398k | return; |
366 | 849k | Frames *frames; |
367 | 849k | TcpSession *ssn = f->protoctx; |
368 | 849k | TcpStream *stream; |
369 | 849k | if (direction == STREAM_TOSERVER) { |
370 | 305k | stream = &ssn->client; |
371 | 305k | frames = &frames_container->toserver; |
372 | 305k | FrameSlide("toserver", frames, stream, slide); |
373 | 543k | } else { |
374 | 543k | stream = &ssn->server; |
375 | 543k | frames = &frames_container->toclient; |
376 | 543k | FrameSlide("toclient", frames, stream, slide); |
377 | 543k | } |
378 | 849k | } |
379 | | |
380 | | static void FrameFreeSingleFrame(Frames *frames, Frame *r) |
381 | 662k | { |
382 | 662k | FrameDebug("free", frames, r); |
383 | 662k | FrameClean(r); |
384 | 662k | } |
385 | | |
386 | | static void FramesClear(Frames *frames) |
387 | 453k | { |
388 | 453k | BUG_ON(frames == NULL); |
389 | | |
390 | 453k | SCLogDebug("frames %u", frames->cnt); |
391 | 1.11M | for (uint16_t i = 0; i < frames->cnt; i++) { |
392 | 662k | if (i < FRAMES_STATIC_CNT) { |
393 | 377k | Frame *r = &frames->sframes[i]; |
394 | 377k | SCLogDebug("removing frame %p", r); |
395 | 377k | FrameFreeSingleFrame(frames, r); |
396 | 377k | } else { |
397 | 285k | const uint16_t o = i - FRAMES_STATIC_CNT; |
398 | 285k | Frame *r = &frames->dframes[o]; |
399 | 285k | SCLogDebug("removing frame %p", r); |
400 | 285k | FrameFreeSingleFrame(frames, r); |
401 | 285k | } |
402 | 662k | } |
403 | 453k | frames->cnt = 0; |
404 | 453k | } |
405 | | |
406 | | void FramesFree(Frames *frames) |
407 | 233k | { |
408 | 233k | BUG_ON(frames == NULL); |
409 | 233k | FramesClear(frames); |
410 | 233k | SCFree(frames->dframes); |
411 | 233k | frames->dframes = NULL; |
412 | 233k | } |
413 | | |
414 | | /** \brief create new frame using a pointer to start of the frame |
415 | | */ |
416 | | Frame *AppLayerFrameNewByPointer(Flow *f, const StreamSlice *stream_slice, |
417 | | const uint8_t *frame_start, const int64_t len, int dir, uint8_t frame_type) |
418 | 771k | { |
419 | 771k | SCLogDebug("frame_start:%p stream_slice->input:%p stream_slice->offset:%" PRIu64, frame_start, |
420 | 771k | stream_slice->input, stream_slice->offset); |
421 | | |
422 | 771k | if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type))) |
423 | 654k | return NULL; |
424 | | |
425 | | /* workarounds for many (unit|fuzz)tests not handling TCP data properly */ |
426 | 117k | #if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
427 | 117k | if (f->proto == IPPROTO_TCP && f->protoctx == NULL) |
428 | 0 | return NULL; |
429 | 117k | if (frame_start < stream_slice->input || |
430 | 117k | frame_start >= stream_slice->input + stream_slice->input_len) |
431 | 0 | return NULL; |
432 | 117k | #endif |
433 | 117k | BUG_ON(frame_start < stream_slice->input); |
434 | 117k | BUG_ON(stream_slice->input == NULL); |
435 | 117k | BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL); |
436 | | |
437 | 117k | ptrdiff_t ptr_offset = frame_start - stream_slice->input; |
438 | | #ifdef DEBUG |
439 | | uint64_t offset = ptr_offset + stream_slice->offset; |
440 | | SCLogDebug("flow %p direction %s frame %p starting at %" PRIu64 " len %" PRIi64 |
441 | | " (offset %" PRIu64 ")", |
442 | | f, dir == 0 ? "toserver" : "toclient", frame_start, offset, len, stream_slice->offset); |
443 | | #endif |
444 | 117k | BUG_ON(f->alparser == NULL); |
445 | | |
446 | 117k | FramesContainer *frames_container = AppLayerFramesSetupContainer(f); |
447 | 117k | if (frames_container == NULL) |
448 | 0 | return NULL; |
449 | | |
450 | 117k | Frames *frames; |
451 | 117k | if (dir == 0) { |
452 | 22.7k | frames = &frames_container->toserver; |
453 | 95.0k | } else { |
454 | 95.0k | frames = &frames_container->toclient; |
455 | 95.0k | } |
456 | | |
457 | 117k | uint64_t abs_frame_offset = stream_slice->offset + ptr_offset; |
458 | | |
459 | 117k | Frame *r = FrameNew(frames, abs_frame_offset, len); |
460 | 117k | if (r != NULL) { |
461 | 117k | r->type = frame_type; |
462 | 117k | FrameDebug("new_by_ptr", frames, r); |
463 | 117k | } |
464 | 117k | return r; |
465 | 117k | } |
466 | | |
467 | | static Frame *AppLayerFrameUdp(Flow *f, const StreamSlice *stream_slice, |
468 | | const uint32_t frame_start_rel, const int64_t len, int dir, uint8_t frame_type) |
469 | 265k | { |
470 | 265k | BUG_ON(f->proto != IPPROTO_UDP); |
471 | | |
472 | 265k | if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type))) |
473 | 0 | return NULL; |
474 | | |
475 | 265k | FramesContainer *frames_container = AppLayerFramesSetupContainer(f); |
476 | 265k | if (frames_container == NULL) |
477 | 0 | return NULL; |
478 | | |
479 | 265k | Frames *frames; |
480 | 265k | if (dir == 0) { |
481 | 228k | frames = &frames_container->toserver; |
482 | 228k | } else { |
483 | 37.5k | frames = &frames_container->toclient; |
484 | 37.5k | } |
485 | | |
486 | 265k | Frame *r = FrameNew(frames, frame_start_rel, len); |
487 | 265k | if (r != NULL) { |
488 | 265k | r->type = frame_type; |
489 | 265k | } |
490 | 265k | return r; |
491 | 265k | } |
492 | | |
493 | | /** \brief create new frame using a relative offset from the start of the stream slice |
494 | | */ |
495 | | Frame *AppLayerFrameNewByRelativeOffset(Flow *f, const StreamSlice *stream_slice, |
496 | | const uint32_t frame_start_rel, const int64_t len, int dir, uint8_t frame_type) |
497 | 151M | { |
498 | 151M | if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type))) |
499 | 150M | return NULL; |
500 | | |
501 | | /* workarounds for many (unit|fuzz)tests not handling TCP data properly */ |
502 | 951k | #if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
503 | 951k | if (f->proto == IPPROTO_TCP && f->protoctx == NULL) |
504 | 0 | return NULL; |
505 | 951k | if (stream_slice->input == NULL) |
506 | 0 | return NULL; |
507 | | #else |
508 | | BUG_ON(stream_slice->input == NULL); |
509 | | #endif |
510 | 951k | BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL); |
511 | 951k | BUG_ON(f->alparser == NULL); |
512 | | |
513 | 951k | if (f->proto == IPPROTO_UDP) { |
514 | 25.2k | return AppLayerFrameUdp(f, stream_slice, frame_start_rel, len, dir, frame_type); |
515 | 25.2k | } |
516 | | |
517 | 926k | FramesContainer *frames_container = AppLayerFramesSetupContainer(f); |
518 | 926k | if (frames_container == NULL) |
519 | 0 | return NULL; |
520 | | |
521 | 926k | Frames *frames; |
522 | 926k | if (dir == 0) { |
523 | 518k | frames = &frames_container->toserver; |
524 | 518k | } else { |
525 | 408k | frames = &frames_container->toclient; |
526 | 408k | } |
527 | | |
528 | 926k | const uint64_t frame_abs_offset = (uint64_t)frame_start_rel + stream_slice->offset; |
529 | 926k | #ifdef DEBUG_VALIDATION |
530 | 926k | const TcpSession *ssn = f->protoctx; |
531 | 926k | const TcpStream *stream = dir == 0 ? &ssn->client : &ssn->server; |
532 | 926k | BUG_ON(stream_slice->offset != STREAM_APP_PROGRESS(stream)); |
533 | 926k | BUG_ON(frame_abs_offset > STREAM_APP_PROGRESS(stream) + stream_slice->input_len); |
534 | 926k | #endif |
535 | 926k | Frame *r = FrameNew(frames, frame_abs_offset, len); |
536 | 926k | if (r != NULL) { |
537 | 687k | r->type = frame_type; |
538 | 687k | } |
539 | 926k | return r; |
540 | 926k | } |
541 | | |
542 | | void AppLayerFrameDump(Flow *f) |
543 | 1.89M | { |
544 | 1.89M | if (f->proto == IPPROTO_TCP && f->protoctx && f->alparser) { |
545 | 1.77M | FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
546 | 1.77M | if (frames_container != NULL) { |
547 | 1.20M | AppLayerFrameDumpForFrames("toserver::dump", &frames_container->toserver); |
548 | 1.20M | AppLayerFrameDumpForFrames("toclient::dump", &frames_container->toclient); |
549 | 1.20M | } |
550 | 1.77M | } |
551 | 1.89M | } |
552 | | |
553 | | /** \brief create new frame using the absolute offset from the start of the stream |
554 | | */ |
555 | | Frame *AppLayerFrameNewByAbsoluteOffset(Flow *f, const StreamSlice *stream_slice, |
556 | | const uint64_t frame_start, const int64_t len, int dir, uint8_t frame_type) |
557 | 2.24M | { |
558 | 2.24M | if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type))) |
559 | 1.85M | return NULL; |
560 | | |
561 | | /* workarounds for many (unit|fuzz)tests not handling TCP data properly */ |
562 | 393k | #if defined(UNITTESTS) || defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
563 | 393k | if (f->proto == IPPROTO_TCP && f->protoctx == NULL) |
564 | 0 | return NULL; |
565 | 393k | if (stream_slice->input == NULL) |
566 | 1 | return NULL; |
567 | | #else |
568 | | BUG_ON(stream_slice->input == NULL); |
569 | | #endif |
570 | 393k | BUG_ON(f->proto == IPPROTO_TCP && f->protoctx == NULL); |
571 | 393k | BUG_ON(f->alparser == NULL); |
572 | 393k | BUG_ON(frame_start < stream_slice->offset); |
573 | 393k | BUG_ON(frame_start - stream_slice->offset >= (uint64_t)INT_MAX); |
574 | | |
575 | 393k | FramesContainer *frames_container = AppLayerFramesSetupContainer(f); |
576 | 393k | if (frames_container == NULL) |
577 | 0 | return NULL; |
578 | | |
579 | 393k | Frames *frames; |
580 | 393k | if (dir == 0) { |
581 | 195k | frames = &frames_container->toserver; |
582 | 197k | } else { |
583 | 197k | frames = &frames_container->toclient; |
584 | 197k | } |
585 | | |
586 | 393k | SCLogDebug("flow %p direction %s frame type %u offset %" PRIu64 " len %" PRIi64 |
587 | 393k | " (slice offset %" PRIu64 ")", |
588 | 393k | f, dir == 0 ? "toserver" : "toclient", frame_type, frame_start, len, |
589 | 393k | stream_slice->offset); |
590 | 393k | Frame *r = FrameNew(frames, frame_start, len); |
591 | 393k | if (r != NULL) { |
592 | 391k | r->type = frame_type; |
593 | 391k | } |
594 | 393k | return r; |
595 | 393k | } |
596 | | |
597 | | void AppLayerFrameAddEvent(Frame *r, uint8_t e) |
598 | 3.92k | { |
599 | 3.92k | if (r != NULL) { |
600 | 3.92k | if (r->event_cnt < 4) { // TODO |
601 | 3.92k | r->events[r->event_cnt++] = e; |
602 | 3.92k | } |
603 | 3.92k | FrameDebug("add_event", NULL, r); |
604 | 3.92k | } |
605 | 3.92k | } |
606 | | |
607 | | void AppLayerFrameAddEventById(Flow *f, const int dir, const FrameId id, uint8_t e) |
608 | 3.36k | { |
609 | 3.36k | Frame *frame = AppLayerFrameGetById(f, dir, id); |
610 | 3.36k | AppLayerFrameAddEvent(frame, e); |
611 | 3.36k | } |
612 | | |
613 | | FrameId AppLayerFrameGetId(Frame *r) |
614 | 151M | { |
615 | 151M | if (r != NULL) { |
616 | 712k | return r->id; |
617 | 150M | } else { |
618 | 150M | return -1; |
619 | 150M | } |
620 | 151M | } |
621 | | |
622 | | void AppLayerFrameSetLength(Frame *frame, int64_t len) |
623 | 15.8k | { |
624 | 15.8k | if (frame != NULL) { |
625 | 15.8k | frame->len = len; |
626 | 15.8k | FrameDebug("set_length", NULL, frame); |
627 | 15.8k | } |
628 | 15.8k | } |
629 | | |
630 | | void AppLayerFrameSetLengthById(Flow *f, const int dir, const FrameId id, int64_t len) |
631 | 0 | { |
632 | 0 | Frame *frame = AppLayerFrameGetById(f, dir, id); |
633 | 0 | AppLayerFrameSetLength(frame, len); |
634 | 0 | } |
635 | | |
636 | | void AppLayerFrameSetTxId(Frame *r, uint64_t tx_id) |
637 | 1.18M | { |
638 | 1.18M | if (r != NULL) { |
639 | 1.18M | r->flags |= FRAME_FLAG_TX_ID_SET; |
640 | 1.18M | r->tx_id = tx_id; |
641 | 1.18M | FrameDebug("set_txid", NULL, r); |
642 | 1.18M | } |
643 | 1.18M | } |
644 | | |
645 | | void AppLayerFrameSetTxIdById(Flow *f, const int dir, const FrameId id, uint64_t tx_id) |
646 | 0 | { |
647 | 0 | Frame *frame = AppLayerFrameGetById(f, dir, id); |
648 | 0 | AppLayerFrameSetTxId(frame, tx_id); |
649 | 0 | } |
650 | | |
651 | | Frame *AppLayerFrameGetById(Flow *f, const int dir, const FrameId frame_id) |
652 | 1.27M | { |
653 | 1.27M | FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
654 | 1.27M | SCLogDebug("get frame_id %" PRIi64 " direction %u/%s frames_container %p", frame_id, dir, |
655 | 1.27M | dir == 0 ? "toserver" : "toclient", frames_container); |
656 | 1.27M | if (frames_container == NULL) |
657 | 457k | return NULL; |
658 | | |
659 | 813k | Frames *frames; |
660 | 813k | if (dir == 0) { |
661 | 477k | frames = &frames_container->toserver; |
662 | 477k | } else { |
663 | 335k | frames = &frames_container->toclient; |
664 | 335k | } |
665 | 813k | SCLogDebug("frames %p", frames); |
666 | 813k | return FrameGetById(frames, frame_id); |
667 | 1.27M | } |
668 | | |
669 | | static inline bool FrameIsDone( |
670 | | const Frame *frame, const uint64_t abs_offset, const uint64_t abs_right_edge) |
671 | 97.0M | { |
672 | | /* frame with negative length means we don't know the size yet. */ |
673 | 97.0M | if (frame->len < 0) |
674 | 4.41M | return false; |
675 | | |
676 | 92.6M | const int64_t frame_abs_offset = frame->offset; |
677 | 92.6M | const int64_t frame_right_edge = frame_abs_offset + frame->len; |
678 | 92.6M | if ((uint64_t)frame_right_edge <= abs_right_edge) { |
679 | 2.88M | SCLogDebug("frame %p id %" PRIi64 " is done", frame, frame->id); |
680 | 2.88M | return true; |
681 | 2.88M | } |
682 | 89.7M | return false; |
683 | 92.6M | } |
684 | | |
685 | | static void FramePrune(Frames *frames, const TcpStream *stream, const bool eof) |
686 | 3.24M | { |
687 | 3.24M | const uint64_t frames_le_start = (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream); |
688 | 3.24M | SCLogDebug("start: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64, |
689 | 3.24M | (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel, |
690 | 3.24M | STREAM_BASE_OFFSET(stream)); |
691 | 3.24M | const uint64_t abs_offset = STREAM_BASE_OFFSET(stream); |
692 | 3.24M | const uint64_t acked = StreamTcpGetUsable(stream, eof); |
693 | 3.24M | uint64_t le = STREAM_APP_PROGRESS(stream); |
694 | | |
695 | 3.24M | const uint16_t start = frames->cnt; |
696 | 3.24M | uint16_t removed = 0; |
697 | 3.24M | uint16_t x = 0; |
698 | 17.6M | for (uint16_t i = 0; i < frames->cnt; i++) { |
699 | 14.4M | if (i < FRAMES_STATIC_CNT) { |
700 | 3.37M | Frame *frame = &frames->sframes[i]; |
701 | 3.37M | FrameDebug("prune(s)", frames, frame); |
702 | 3.37M | if (eof || FrameIsDone(frame, abs_offset, acked)) { |
703 | | // remove by not incrementing 'x' |
704 | 684k | SCLogDebug("removing %p id %" PRIi64, frame, frame->id); |
705 | 684k | FrameDebug("remove(s)", frames, frame); |
706 | 684k | FrameClean(frame); |
707 | 684k | removed++; |
708 | 2.69M | } else { |
709 | 2.69M | const uint64_t fle = FrameLeftEdge(stream, frame); |
710 | 2.69M | le = MIN(le, fle); |
711 | 2.69M | SCLogDebug("le %" PRIu64 ", frame fle %" PRIu64, le, fle); |
712 | 2.69M | Frame *nframe = &frames->sframes[x]; |
713 | 2.69M | FrameCopy(nframe, frame); |
714 | 2.69M | if (frame != nframe) { |
715 | 11.6k | FrameClean(frame); |
716 | 11.6k | } |
717 | 2.69M | x++; |
718 | 2.69M | } |
719 | 11.0M | } else { |
720 | 11.0M | const uint16_t o = i - FRAMES_STATIC_CNT; |
721 | 11.0M | Frame *frame = &frames->dframes[o]; |
722 | 11.0M | FrameDebug("prune(d)", frames, frame); |
723 | 11.0M | if (eof || FrameIsDone(frame, abs_offset, acked)) { |
724 | | // remove by not incrementing 'x' |
725 | 148k | SCLogDebug("removing %p id %" PRIi64, frame, frame->id); |
726 | 148k | FrameDebug("remove(d)", frames, frame); |
727 | 148k | FrameClean(frame); |
728 | 148k | removed++; |
729 | 10.9M | } else { |
730 | 10.9M | const uint64_t fle = FrameLeftEdge(stream, frame); |
731 | 10.9M | le = MIN(le, fle); |
732 | 10.9M | SCLogDebug("le %" PRIu64 ", frame fle %" PRIu64, le, fle); |
733 | 10.9M | Frame *nframe; |
734 | 10.9M | if (x >= FRAMES_STATIC_CNT) { |
735 | 10.9M | nframe = &frames->dframes[x - FRAMES_STATIC_CNT]; |
736 | 10.9M | } else { |
737 | 5.48k | nframe = &frames->sframes[x]; |
738 | 5.48k | } |
739 | 10.9M | FrameCopy(nframe, frame); |
740 | 10.9M | if (frame != nframe) { |
741 | 9.38k | FrameClean(frame); |
742 | 9.38k | } |
743 | 10.9M | x++; |
744 | 10.9M | } |
745 | 11.0M | } |
746 | 14.4M | } |
747 | 3.24M | frames->cnt = x; |
748 | 3.24M | frames->left_edge_rel = le - STREAM_BASE_OFFSET(stream); |
749 | | #ifdef DEBUG |
750 | | SCLogDebug("end: left edge %" PRIu64 ", left_edge_rel %u, stream base %" PRIu64 |
751 | | ", cnt %u, removed %u, start %u", |
752 | | (uint64_t)frames->left_edge_rel + STREAM_BASE_OFFSET(stream), frames->left_edge_rel, |
753 | | STREAM_BASE_OFFSET(stream), frames->cnt, removed, start); |
754 | | AppLayerFrameDumpForFrames("post_slide", frames); |
755 | | #endif |
756 | 3.24M | BUG_ON(le < STREAM_BASE_OFFSET(stream)); |
757 | 3.24M | if (frames->cnt > 0) { // if we removed all this can fail |
758 | 1.60M | BUG_ON(frames_le_start > le); |
759 | 1.60M | } |
760 | 3.24M | BUG_ON(x != start - removed); |
761 | 3.24M | } |
762 | | |
763 | | void FramesPrune(Flow *f, Packet *p) |
764 | 7.43M | { |
765 | 7.43M | if (f->proto == IPPROTO_TCP && f->protoctx == NULL) |
766 | 0 | return; |
767 | 7.43M | FramesContainer *frames_container = AppLayerFramesGetContainer(f); |
768 | 7.43M | if (frames_container == NULL) |
769 | 4.17M | return; |
770 | | |
771 | 3.26M | Frames *frames; |
772 | | |
773 | 3.26M | if (p->proto == IPPROTO_UDP) { |
774 | 19.2k | SCLogDebug("clearing all UDP frames"); |
775 | 19.2k | if (PKT_IS_TOSERVER(p)) { |
776 | 14.0k | frames = &frames_container->toserver; |
777 | 14.0k | } else { |
778 | 5.26k | frames = &frames_container->toclient; |
779 | 5.26k | } |
780 | 19.2k | FramesClear(frames); |
781 | 19.2k | return; |
782 | 19.2k | } |
783 | | |
784 | 3.24M | TcpSession *ssn = f->protoctx; |
785 | | |
786 | 3.24M | if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) { |
787 | 3.89k | AppLayerFramesFreeContainer(f); |
788 | 3.89k | return; |
789 | 3.89k | } |
790 | | |
791 | 3.24M | TcpStream *stream; |
792 | 3.24M | if (PKT_IS_TOSERVER(p)) { |
793 | 1.68M | stream = &ssn->client; |
794 | 1.68M | frames = &frames_container->toserver; |
795 | 1.68M | } else { |
796 | 1.55M | stream = &ssn->server; |
797 | 1.55M | frames = &frames_container->toclient; |
798 | 1.55M | } |
799 | | |
800 | 3.24M | const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p); |
801 | 3.24M | SCLogDebug("eof %s", eof ? "TRUE" : "false"); |
802 | 3.24M | FramePrune(frames, stream, eof); |
803 | 3.24M | } |