suricata
flow-spare-pool.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2023 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  * Flow queue handler functions
24  */
25 
26 #include "suricata-common.h"
27 #include "threads.h"
28 #include "flow-private.h"
29 #include "flow-queue.h"
30 #include "flow-util.h"
31 #include "flow-spare-pool.h"
32 #include "util-error.h"
33 #include "util-debug.h"
34 #include "util-print.h"
35 #include "util-validate.h"
36 
37 typedef struct FlowSparePool {
41 
42 static uint32_t flow_spare_pool_flow_cnt = 0;
43 static FlowSparePool *flow_spare_pool = NULL;
44 static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER;
45 
46 uint32_t FlowSpareGetPoolSize(void)
47 {
48  uint32_t size;
49  SCMutexLock(&flow_spare_pool_m);
50  size = flow_spare_pool_flow_cnt;
51  SCMutexUnlock(&flow_spare_pool_m);
52  return size;
53 }
54 
55 static FlowSparePool *FlowSpareGetPool(void)
56 {
57  FlowSparePool *p = SCCalloc(1, sizeof(*p));
58  if (p == NULL)
59  return NULL;
60  return p;
61 }
62 
63 static bool FlowSparePoolUpdateBlock(FlowSparePool *p)
64 {
65  DEBUG_VALIDATE_BUG_ON(p == NULL);
66 
67  for (uint32_t i = p->queue.len; i < FLOW_SPARE_POOL_BLOCK_SIZE; i++) {
68  Flow *f = FlowAlloc();
69  if (f == NULL)
70  return false;
72  }
73  return true;
74 }
75 
76 #ifdef FSP_VALIDATE
77 static void Validate(FlowSparePool *top, const uint32_t target)
78 {
79  if (top == NULL) {
80  assert(target == 0);
81  return;
82  }
83 
84  assert(top->queue.len >= 1);
85  // if (top->next != NULL)
86  // assert(top->next->queue.len == FLOW_SPARE_POOL_BLOCK_SIZE);
87 
88  uint32_t cnt = 0;
89  for (FlowSparePool *p = top; p != NULL; p = p->next)
90  {
91  assert(p->queue.len);
92  cnt += p->queue.len;
93  }
94  assert(cnt == target);
95 }
96 #endif
97 
99 {
100  SCMutexLock(&flow_spare_pool_m);
101  if (flow_spare_pool == NULL) {
102  flow_spare_pool = FlowSpareGetPool();
103  }
104  DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL);
105 
106  /* if the top is full, get a new block */
107  if (flow_spare_pool->queue.len >= FLOW_SPARE_POOL_BLOCK_SIZE) {
108  FlowSparePool *p = FlowSpareGetPool();
109  DEBUG_VALIDATE_BUG_ON(p == NULL);
110  p->next = flow_spare_pool;
111  flow_spare_pool = p;
112  }
113  /* add to the (possibly new) top */
114  FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f);
115  flow_spare_pool_flow_cnt++;
116 
117  SCMutexUnlock(&flow_spare_pool_m);
118 }
119 
121 {
122  FlowSparePool *p = FlowSpareGetPool();
123  DEBUG_VALIDATE_BUG_ON(p == NULL);
124  p->queue = *fqp;
125 
126  SCMutexLock(&flow_spare_pool_m);
127  flow_spare_pool_flow_cnt += fqp->len;
128  if (flow_spare_pool != NULL) {
130  /* full block insert */
131 
132  if (flow_spare_pool->queue.len < FLOW_SPARE_POOL_BLOCK_SIZE) {
133  p->next = flow_spare_pool->next;
134  flow_spare_pool->next = p;
135  p = NULL;
136  } else {
137  p->next = flow_spare_pool;
138  flow_spare_pool = p;
139  p = NULL;
140  }
141  } else {
142  /* incomplete block insert */
143 
144  if (p->queue.len + flow_spare_pool->queue.len <= FLOW_SPARE_POOL_BLOCK_SIZE) {
145  FlowQueuePrivateAppendPrivate(&flow_spare_pool->queue, &p->queue);
146  /* free 'p' outside of lock below */
147  } else {
148  // put smallest first
149  if (p->queue.len < flow_spare_pool->queue.len) {
150  p->next = flow_spare_pool;
151  flow_spare_pool = p;
152  } else {
153  p->next = flow_spare_pool->next;
154  flow_spare_pool->next = p;
155  }
156  p = NULL;
157  }
158  }
159  } else {
160  p->next = flow_spare_pool;
161  flow_spare_pool = p;
162  p = NULL;
163  }
164  SCMutexUnlock(&flow_spare_pool_m);
165 
166  FlowQueuePrivate empty = { NULL, NULL, 0 };
167  *fqp = empty;
168 
169  if (p != NULL)
170  SCFree(p);
171 }
172 
174 {
175  SCMutexLock(&flow_spare_pool_m);
176  if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) {
177  SCMutexUnlock(&flow_spare_pool_m);
178  FlowQueuePrivate empty = { NULL, NULL, 0 };
179  return empty;
180  }
181 
182  /* top if full or its the only block we have */
183  if (flow_spare_pool->queue.len >= FLOW_SPARE_POOL_BLOCK_SIZE || flow_spare_pool->next == NULL) {
184  FlowSparePool *p = flow_spare_pool;
185  flow_spare_pool = p->next;
186  DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
187  flow_spare_pool_flow_cnt -= p->queue.len;
188 #ifdef FSP_VALIDATE
189  Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
190 #endif
191  SCMutexUnlock(&flow_spare_pool_m);
192 
193  FlowQueuePrivate ret = p->queue;
194  SCFree(p);
195  return ret;
196  /* next should always be full if it exists */
197  } else if (flow_spare_pool->next != NULL) {
198  FlowSparePool *p = flow_spare_pool->next;
199  flow_spare_pool->next = p->next;
200  DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
201  flow_spare_pool_flow_cnt -= p->queue.len;
202 #ifdef FSP_VALIDATE
203  Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
204 #endif
205  SCMutexUnlock(&flow_spare_pool_m);
206 
207  FlowQueuePrivate ret = p->queue;
208  SCFree(p);
209  return ret;
210  }
211 
212  SCMutexUnlock(&flow_spare_pool_m);
213  FlowQueuePrivate empty = { NULL, NULL, 0 };
214  return empty;
215 }
216 
217 void FlowSparePoolUpdate(uint32_t size)
218 {
219  const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size;
220  if (todo < 0) {
221  uint32_t to_remove = (uint32_t)(todo * -1) / 10;
222  while (to_remove) {
223  if (to_remove < FLOW_SPARE_POOL_BLOCK_SIZE)
224  return;
225 
226  FlowSparePool *p = NULL;
227  SCMutexLock(&flow_spare_pool_m);
228  p = flow_spare_pool;
229  if (p != NULL) {
230  flow_spare_pool = p->next;
231  flow_spare_pool_flow_cnt -= p->queue.len;
232  to_remove -= p->queue.len;
233  }
234  SCMutexUnlock(&flow_spare_pool_m);
235 
236  if (p != NULL) {
237  Flow *f;
238  while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
239  FlowFree(f);
240  }
241  SCFree(p);
242  }
243  }
244  } else if (todo > 0) {
245  FlowSparePool *head = NULL, *tail = NULL;
246 
247  uint32_t blocks = ((uint32_t)todo / FLOW_SPARE_POOL_BLOCK_SIZE) + 1;
248 
249  uint32_t flow_cnt = 0;
250  for (uint32_t cnt = 0; cnt < blocks; cnt++) {
251  FlowSparePool *p = FlowSpareGetPool();
252  if (p == NULL) {
253  break;
254  }
255  const bool ok = FlowSparePoolUpdateBlock(p);
256  if (p->queue.len == 0) {
257  SCFree(p);
258  break;
259  }
260  flow_cnt += p->queue.len;
261 
262  /* prepend to list */
263  p->next = head;
264  head = p;
265  if (tail == NULL)
266  tail = p;
267  if (!ok)
268  break;
269  }
270  if (head) {
271  SCMutexLock(&flow_spare_pool_m);
272  if (flow_spare_pool == NULL) {
273  flow_spare_pool = head;
274  } else if (tail != NULL) {
275  /* since these are 'full' buckets we don't put them
276  * at the top but right after as the top is likely not
277  * full. */
278  tail->next = flow_spare_pool->next;
279  flow_spare_pool->next = head;
280  }
281 
282  flow_spare_pool_flow_cnt += flow_cnt;
283 #ifdef FSP_VALIDATE
284  Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
285 #endif
286  SCMutexUnlock(&flow_spare_pool_m);
287  }
288  }
289 }
290 
292 {
293  SCMutexLock(&flow_spare_pool_m);
294  for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) {
295  FlowSparePool *p = FlowSpareGetPool();
296  if (p == NULL) {
297  FatalError("failed to initialize flow pool");
298  }
299  FlowSparePoolUpdateBlock(p);
300  cnt += p->queue.len;
301 
302  /* prepend to list */
303  p->next = flow_spare_pool;
304  flow_spare_pool = p;
305  flow_spare_pool_flow_cnt = cnt;
306  }
307  SCMutexUnlock(&flow_spare_pool_m);
308 }
309 
311 {
312  SCMutexLock(&flow_spare_pool_m);
313  for (FlowSparePool *p = flow_spare_pool; p != NULL; ) {
314  uint32_t cnt = 0;
315  Flow *f;
316  while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
317  FlowFree(f);
318  cnt++;
319  }
320  flow_spare_pool_flow_cnt -= cnt;
321  FlowSparePool *next = p->next;
322  SCFree(p);
323  p = next;
324  }
325  flow_spare_pool = NULL;
326  SCMutexUnlock(&flow_spare_pool_m);
327 }
FlowSparePoolUpdate
void FlowSparePoolUpdate(uint32_t size)
Definition: flow-spare-pool.c:217
FlowSparePoolReturnFlow
void FlowSparePoolReturnFlow(Flow *f)
Definition: flow-spare-pool.c:98
FlowSpareGetPoolSize
uint32_t FlowSpareGetPoolSize(void)
Definition: flow-spare-pool.c:46
flow-util.h
FlowSpareGetFromPool
FlowQueuePrivate FlowSpareGetFromPool(void)
Definition: flow-spare-pool.c:173
FlowSparePool
Definition: flow-spare-pool.c:37
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
threads.h
flow-private.h
Flow_
Flow data structure.
Definition: flow.h:360
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:121
FlowQueuePrivate_::len
uint32_t len
Definition: flow-queue.h:44
FlowSparePoolReturnFlows
void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp)
Definition: flow-spare-pool.c:120
FlowCnf_::prealloc
uint32_t prealloc
Definition: flow.h:299
flow-spare-pool.h
util-debug.h
util-error.h
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
util-print.h
FlowQueuePrivateGetFromTop
Flow * FlowQueuePrivateGetFromTop(FlowQueuePrivate *fqc)
Definition: flow-queue.c:151
FlowQueuePrivateAppendPrivate
void FlowQueuePrivateAppendPrivate(FlowQueuePrivate *dest, FlowQueuePrivate *src)
Definition: flow-queue.c:88
flow-queue.h
FlowSparePool::queue
FlowQueuePrivate queue
Definition: flow-spare-pool.c:38
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
tail
Host * tail
Definition: host.h:2
suricata-common.h
FlowFree
void FlowFree(Flow *f)
cleanup & free the memory of a flow
Definition: flow-util.c:82
flow_config
FlowConfig flow_config
Definition: flow.c:90
FlowSparePoolDestroy
void FlowSparePoolDestroy(void)
Definition: flow-spare-pool.c:310
FatalError
#define FatalError(...)
Definition: util-debug.h:502
util-validate.h
FlowQueuePrivate_
Definition: flow-queue.h:41
head
Flow * head
Definition: flow-hash.h:1
SCFree
#define SCFree(p)
Definition: util-mem.h:61
FlowSparePool::next
struct FlowSparePool * next
Definition: flow-spare-pool.c:39
FlowSparePoolInit
void FlowSparePoolInit(void)
Definition: flow-spare-pool.c:291
FlowQueuePrivateAppendFlow
void FlowQueuePrivateAppendFlow(FlowQueuePrivate *fqc, Flow *f)
Definition: flow-queue.c:65
FlowAlloc
Flow * FlowAlloc(void)
allocate a flow
Definition: flow-util.c:54
FlowSparePool
struct FlowSparePool FlowSparePool
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
FLOW_SPARE_POOL_BLOCK_SIZE
#define FLOW_SPARE_POOL_BLOCK_SIZE
Definition: flow-spare-pool.h:30
SCMutex
#define SCMutex
Definition: threads-debug.h:114
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102