1 /* Copyright (C) 2007-2020 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 "debug.h"
29 #include "flow-private.h"
30 #include "flow-queue.h"
31 #include "flow-util.h"
32 #include "flow-spare-pool.h"
33 #include "util-error.h"
34 #include "util-debug.h"
35 #include "util-print.h"
36 #include "util-validate.h"
37 
38 typedef struct FlowSparePool {
39     FlowQueuePrivate queue;
40     struct FlowSparePool *next;
41 } FlowSparePool;
42 
43 static uint32_t flow_spare_pool_flow_cnt = 0;
44 static uint32_t flow_spare_pool_block_size = 100;
45 static FlowSparePool *flow_spare_pool = NULL;
46 static SCMutex flow_spare_pool_m = SCMUTEX_INITIALIZER;
47 
FlowSpareGetPoolSize(void)48 uint32_t FlowSpareGetPoolSize(void)
49 {
50     uint32_t size;
51     SCMutexLock(&flow_spare_pool_m);
52     size = flow_spare_pool_flow_cnt;
53     SCMutexUnlock(&flow_spare_pool_m);
54     return size;
55 }
56 
FlowSpareGetPool(void)57 static FlowSparePool *FlowSpareGetPool(void)
58 {
59     FlowSparePool *p = SCCalloc(1, sizeof(*p));
60     if (p == NULL)
61         return NULL;
62     return p;
63 }
64 
FlowSparePoolUpdateBlock(FlowSparePool * p)65 static bool FlowSparePoolUpdateBlock(FlowSparePool *p)
66 {
67     DEBUG_VALIDATE_BUG_ON(p == NULL);
68 
69     for (uint32_t i = p->queue.len; i < flow_spare_pool_block_size; i++)
70     {
71         Flow *f = FlowAlloc();
72         if (f == NULL)
73             return false;
74         FlowQueuePrivateAppendFlow(&p->queue, f);
75     }
76     return true;
77 }
78 
79 #ifdef FSP_VALIDATE
Validate(FlowSparePool * top,const uint32_t target)80 static void Validate(FlowSparePool *top, const uint32_t target)
81 {
82     if (top == NULL) {
83         assert(target == 0);
84         return;
85     }
86 
87     assert(top->queue.len >= 1);
88     //if (top->next != NULL)
89     //    assert(top->next->queue.len == flow_spare_pool_block_size);
90 
91     uint32_t cnt = 0;
92     for (FlowSparePool *p = top; p != NULL; p = p->next)
93     {
94         assert(p->queue.len);
95         cnt += p->queue.len;
96     }
97     assert(cnt == target);
98 }
99 #endif
100 
FlowSparePoolReturnFlow(Flow * f)101 void FlowSparePoolReturnFlow(Flow *f)
102 {
103     SCMutexLock(&flow_spare_pool_m);
104     if (flow_spare_pool == NULL) {
105         flow_spare_pool = FlowSpareGetPool();
106     }
107     DEBUG_VALIDATE_BUG_ON(flow_spare_pool == NULL);
108 
109     /* if the top is full, get a new block */
110     if (flow_spare_pool->queue.len >= flow_spare_pool_block_size) {
111         FlowSparePool *p = FlowSpareGetPool();
112         DEBUG_VALIDATE_BUG_ON(p == NULL);
113         p->next = flow_spare_pool;
114         flow_spare_pool = p;
115     }
116     /* add to the (possibly new) top */
117     FlowQueuePrivateAppendFlow(&flow_spare_pool->queue, f);
118     flow_spare_pool_flow_cnt++;
119 
120     SCMutexUnlock(&flow_spare_pool_m);
121 }
122 
FlowSparePoolReturnFlows(FlowQueuePrivate * fqp)123 void FlowSparePoolReturnFlows(FlowQueuePrivate *fqp)
124 {
125 
126 }
127 
FlowSpareGetFromPool(void)128 FlowQueuePrivate FlowSpareGetFromPool(void)
129 {
130     SCMutexLock(&flow_spare_pool_m);
131     if (flow_spare_pool == NULL || flow_spare_pool_flow_cnt == 0) {
132         SCMutexUnlock(&flow_spare_pool_m);
133         FlowQueuePrivate empty = { NULL, NULL, 0 };
134         return empty;
135     }
136 
137     /* top if full or its the only block we have */
138     if (flow_spare_pool->queue.len >= flow_spare_pool_block_size || flow_spare_pool->next == NULL) {
139         FlowSparePool *p = flow_spare_pool;
140         flow_spare_pool = p->next;
141         DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
142         flow_spare_pool_flow_cnt -= p->queue.len;
143 #ifdef FSP_VALIDATE
144         Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
145 #endif
146         SCMutexUnlock(&flow_spare_pool_m);
147 
148         FlowQueuePrivate ret = p->queue;
149         SCFree(p);
150         return ret;
151     /* next should always be full if it exists */
152     } else if (flow_spare_pool->next != NULL) {
153         FlowSparePool *p = flow_spare_pool->next;
154         flow_spare_pool->next = p->next;
155         DEBUG_VALIDATE_BUG_ON(flow_spare_pool_flow_cnt < p->queue.len);
156         flow_spare_pool_flow_cnt -= p->queue.len;
157 #ifdef FSP_VALIDATE
158         Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
159 #endif
160         SCMutexUnlock(&flow_spare_pool_m);
161 
162         FlowQueuePrivate ret = p->queue;
163         SCFree(p);
164         return ret;
165     }
166 
167     SCMutexUnlock(&flow_spare_pool_m);
168     FlowQueuePrivate empty = { NULL, NULL, 0 };
169     return empty;
170 }
171 
FlowSparePoolUpdate(uint32_t size)172 void FlowSparePoolUpdate(uint32_t size)
173 {
174     const int64_t todo = (int64_t)flow_config.prealloc - (int64_t)size;
175     if (todo < 0) {
176         uint32_t to_remove = (uint32_t)(todo * -1) / 10;
177         while (to_remove) {
178             if (to_remove < flow_spare_pool_block_size)
179                 return;
180 
181             FlowSparePool *p = NULL;
182             SCMutexLock(&flow_spare_pool_m);
183             p = flow_spare_pool;
184             if (p != NULL) {
185                 flow_spare_pool = p->next;
186                 flow_spare_pool_flow_cnt -= p->queue.len;
187                 to_remove -= p->queue.len;
188             }
189             SCMutexUnlock(&flow_spare_pool_m);
190 
191             if (p != NULL) {
192                 Flow *f;
193                 while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
194                     FlowFree(f);
195                 }
196                 SCFree(p);
197             }
198         }
199     } else if (todo > 0) {
200         FlowSparePool *head = NULL, *tail = NULL;
201 
202         uint32_t blocks = ((uint32_t)todo / flow_spare_pool_block_size) + 1;
203 
204         uint32_t flow_cnt = 0;
205         for (uint32_t cnt = 0; cnt < blocks; cnt++) {
206             FlowSparePool *p = FlowSpareGetPool();
207             if (p == NULL) {
208                 break;
209             }
210             const bool ok = FlowSparePoolUpdateBlock(p);
211             if (p->queue.len == 0) {
212                 SCFree(p);
213                 break;
214             }
215             flow_cnt += p->queue.len;
216 
217             /* prepend to list */
218             p->next = head;
219             head = p;
220             if (tail == NULL)
221                 tail = p;
222             if (!ok)
223                 break;
224         }
225         if (head) {
226             SCMutexLock(&flow_spare_pool_m);
227             if (flow_spare_pool == NULL) {
228                 flow_spare_pool = head;
229             } else if (tail != NULL) {
230                 /* since these are 'full' buckets we don't put them
231                  * at the top but right after as the top is likely not
232                  * full. */
233                 tail->next = flow_spare_pool->next;
234                 flow_spare_pool->next = head;
235             }
236 
237             flow_spare_pool_flow_cnt += flow_cnt;
238 #ifdef FSP_VALIDATE
239             Validate(flow_spare_pool, flow_spare_pool_flow_cnt);
240 #endif
241             SCMutexUnlock(&flow_spare_pool_m);
242         }
243     }
244 }
245 
FlowSparePoolInit(void)246 void FlowSparePoolInit(void)
247 {
248     SCMutexLock(&flow_spare_pool_m);
249     for (uint32_t cnt = 0; cnt < flow_config.prealloc; ) {
250         FlowSparePool *p = FlowSpareGetPool();
251         if (p == NULL) {
252             FatalError(SC_ERR_FLOW_INIT, "failed to initialize flow pool");
253         }
254         FlowSparePoolUpdateBlock(p);
255         cnt += p->queue.len;
256 
257         /* prepend to list */
258         p->next = flow_spare_pool;
259         flow_spare_pool = p;
260         flow_spare_pool_flow_cnt = cnt;
261     }
262     SCMutexUnlock(&flow_spare_pool_m);
263 }
264 
FlowSparePoolDestroy(void)265 void FlowSparePoolDestroy(void)
266 {
267     SCMutexLock(&flow_spare_pool_m);
268     for (FlowSparePool *p = flow_spare_pool; p != NULL; ) {
269         uint32_t cnt = 0;
270         Flow *f;
271         while ((f = FlowQueuePrivateGetFromTop(&p->queue))) {
272             FlowFree(f);
273             cnt++;
274         }
275         flow_spare_pool_flow_cnt -= cnt;
276         FlowSparePool *next = p->next;
277         SCFree(p);
278         p = next;
279     }
280     flow_spare_pool = NULL;
281     SCMutexUnlock(&flow_spare_pool_m);
282 }
283