suricata
tmqh-packetpool.c
Go to the documentation of this file.
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  * Packetpool queue handlers. Packet pool is implemented as a stack.
24  */
25 
26 #include "suricata-common.h"
27 #include "tmqh-packetpool.h"
28 #include "tm-queuehandlers.h"
29 #include "tm-threads.h"
30 #include "threads.h"
31 #include "decode.h"
32 #include "tm-modules.h"
33 #include "packet.h"
34 #include "util-profiling.h"
35 #include "util-validate.h"
36 #include "action-globals.h"
37 
38 extern uint16_t max_pending_packets;
39 
40 /* Number of freed packet to save for one pool before freeing them. */
41 #define MAX_PENDING_RETURN_PACKETS 32
42 static uint32_t max_pending_return_packets = MAX_PENDING_RETURN_PACKETS;
43 
44 thread_local PktPool thread_pkt_pool;
45 
46 static inline PktPool *GetThreadPacketPool(void)
47 {
48  return &thread_pkt_pool;
49 }
50 
51 /**
52  * \brief TmqhPacketpoolRegister
53  * \initonly
54  */
56 {
57  tmqh_table[TMQH_PACKETPOOL].name = "packetpool";
60 }
61 
62 static int PacketPoolIsEmpty(PktPool *pool)
63 {
64  /* Check local stack first. */
65  if (pool->head || pool->return_stack.head)
66  return 0;
67 
68  return 1;
69 }
70 
71 static void UpdateReturnThreshold(PktPool *pool)
72 {
73  const float perc = (float)pool->cnt / (float)max_pending_packets;
74  uint32_t threshold = (uint32_t)(perc * (float)max_pending_return_packets);
75  if (threshold != SC_ATOMIC_GET(pool->return_stack.return_threshold)) {
76  SC_ATOMIC_SET(pool->return_stack.return_threshold, threshold);
77  }
78 }
79 
80 void PacketPoolWait(void)
81 {
82  PktPool *my_pool = GetThreadPacketPool();
83 
84  if (PacketPoolIsEmpty(my_pool)) {
85  SC_ATOMIC_SET(my_pool->return_stack.return_threshold, 1);
86 
87  SCMutexLock(&my_pool->return_stack.mutex);
88  SCCondWait(&my_pool->return_stack.cond, &my_pool->return_stack.mutex);
89  SCMutexUnlock(&my_pool->return_stack.mutex);
90 
91  UpdateReturnThreshold(my_pool);
92  }
93 
94  while(PacketPoolIsEmpty(my_pool))
95  cc_barrier();
96 }
97 
98 /** \brief a initialized packet
99  *
100  * \warning Use *only* at init, not at packet runtime
101  */
102 static void PacketPoolStorePacket(Packet *p)
103 {
104  p->pool = GetThreadPacketPool();
107 }
108 
109 static void PacketPoolGetReturnedPackets(PktPool *pool)
110 {
111  SCMutexLock(&pool->return_stack.mutex);
112  /* Move all the packets from the locked return stack to the local stack. */
113  pool->head = pool->return_stack.head;
114  pool->return_stack.head = NULL;
115  pool->cnt += pool->return_stack.cnt;
116  pool->return_stack.cnt = 0;
117  SCMutexUnlock(&pool->return_stack.mutex);
118 }
119 
120 /** \brief Get a new packet from the packet pool
121  *
122  * Only allocates from the thread's local stack, or mallocs new packets.
123  * If the local stack is empty, first move all the return stack packets to
124  * the local stack.
125  * \retval Packet pointer, or NULL on failure.
126  */
128 {
129  PktPool *pool = GetThreadPacketPool();
130  DEBUG_VALIDATE_BUG_ON(pool->initialized == 0);
131  DEBUG_VALIDATE_BUG_ON(pool->destroyed == 1);
132  if (pool->head) {
133  /* Stack is not empty. */
134  Packet *p = pool->head;
135  pool->head = p->next;
136  pool->cnt--;
137  p->pool = pool;
138  PacketReinit(p);
139 
140  UpdateReturnThreshold(pool);
141  SCLogDebug("pp: %0.2f cnt:%u max:%d threshold:%u",
142  ((float)pool->cnt / (float)max_pending_packets) * (float)100, pool->cnt,
143  max_pending_packets, SC_ATOMIC_GET(pool->return_stack.return_threshold));
144  return p;
145  }
146 
147  /* Local Stack is empty, so check the return stack, which requires
148  * locking. */
149  PacketPoolGetReturnedPackets(pool);
150 
151  /* Try to allocate again. Need to check for not empty again, since the
152  * return stack might have been empty too.
153  */
154  if (pool->head) {
155  /* Stack is not empty. */
156  Packet *p = pool->head;
157  pool->head = p->next;
158  pool->cnt--;
159  p->pool = pool;
160  PacketReinit(p);
161 
162  UpdateReturnThreshold(pool);
163  SCLogDebug("pp: %0.2f cnt:%u max:%d threshold:%u",
164  ((float)pool->cnt / (float)max_pending_packets) * (float)100, pool->cnt,
165  max_pending_packets, SC_ATOMIC_GET(pool->return_stack.return_threshold));
166  return p;
167  }
168 
169  /* Failed to allocate a packet, so return NULL. */
170  /* Optionally, could allocate a new packet here. */
171  return NULL;
172 }
173 
174 /** \brief Return packet to Packet pool
175  *
176  */
178 {
179  PktPool *my_pool = GetThreadPacketPool();
180  PktPool *pool = p->pool;
181  if (pool == NULL) {
182  PacketFree(p);
183  return;
184  }
185 
187 
188 #ifdef DEBUG_VALIDATION
189  BUG_ON(pool->initialized == 0);
190  BUG_ON(pool->destroyed == 1);
191  BUG_ON(my_pool->initialized == 0);
192  BUG_ON(my_pool->destroyed == 1);
193 #endif /* DEBUG_VALIDATION */
194 
195  if (pool == my_pool) {
196  /* Push back onto this thread's own stack, so no locking. */
197  p->next = my_pool->head;
198  my_pool->head = p;
199  my_pool->cnt++;
200  } else {
201  PktPool *pending_pool = my_pool->pending_pool;
202  if (pending_pool == NULL || pending_pool == pool) {
203  if (pending_pool == NULL) {
204  /* No pending packet, so store the current packet. */
205  p->next = NULL;
206  my_pool->pending_pool = pool;
207  my_pool->pending_head = p;
208  my_pool->pending_tail = p;
209  my_pool->pending_count = 1;
210  } else if (pending_pool == pool) {
211  /* Another packet for the pending pool list. */
212  p->next = my_pool->pending_head;
213  my_pool->pending_head = p;
214  my_pool->pending_count++;
215  }
216 
217  const uint32_t threshold = SC_ATOMIC_GET(pool->return_stack.return_threshold);
218  if (my_pool->pending_count >= threshold) {
219  /* Return the entire list of pending packets. */
220  SCMutexLock(&pool->return_stack.mutex);
221  my_pool->pending_tail->next = pool->return_stack.head;
222  pool->return_stack.head = my_pool->pending_head;
223  pool->return_stack.cnt += my_pool->pending_count;
224  SCCondSignal(&pool->return_stack.cond);
225  SCMutexUnlock(&pool->return_stack.mutex);
226  /* Clear the list of pending packets to return. */
227  my_pool->pending_pool = NULL;
228  my_pool->pending_head = NULL;
229  my_pool->pending_tail = NULL;
230  my_pool->pending_count = 0;
231  }
232  } else {
233  /* Push onto return stack for this pool */
234  SCMutexLock(&pool->return_stack.mutex);
235  p->next = pool->return_stack.head;
236  pool->return_stack.head = p;
237  pool->return_stack.cnt++;
238  SCCondSignal(&pool->return_stack.cond);
239  SCMutexUnlock(&pool->return_stack.mutex);
240  }
241  }
242 }
243 
244 void PacketPoolInit(void)
245 {
246  PktPool *my_pool = GetThreadPacketPool();
247 
248 #ifdef DEBUG_VALIDATION
249  BUG_ON(my_pool->initialized);
250  my_pool->initialized = 1;
251  my_pool->destroyed = 0;
252 #endif /* DEBUG_VALIDATION */
253 
254  SCMutexInit(&my_pool->return_stack.mutex, NULL);
255  SCCondInit(&my_pool->return_stack.cond, NULL);
256  SC_ATOMIC_INIT(my_pool->return_stack.return_threshold);
257  SC_ATOMIC_SET(my_pool->return_stack.return_threshold, 32);
258 
259  /* pre allocate packets */
260  SCLogDebug("preallocating packets... packet size %" PRIuMAX "",
261  (uintmax_t)SIZE_OF_PACKET);
262  int i = 0;
263  for (i = 0; i < max_pending_packets; i++) {
264  Packet *p = PacketGetFromAlloc();
265  if (unlikely(p == NULL)) {
266  FatalError("Fatal error encountered while allocating a packet. Exiting...");
267  }
268  PacketPoolStorePacket(p);
269  }
270 
271  //SCLogInfo("preallocated %"PRIiMAX" packets. Total memory %"PRIuMAX"",
272  // max_pending_packets, (uintmax_t)(max_pending_packets*SIZE_OF_PACKET));
273 }
274 
276 {
277  Packet *p = NULL;
278  PktPool *my_pool = GetThreadPacketPool();
279 
280 #ifdef DEBUG_VALIDATION
281  BUG_ON(my_pool && my_pool->destroyed);
282 #endif /* DEBUG_VALIDATION */
283 
284  if (my_pool && my_pool->pending_pool != NULL) {
285  p = my_pool->pending_head;
286  while (p) {
287  Packet *next_p = p->next;
288  PacketFree(p);
289  p = next_p;
290  my_pool->pending_count--;
291  }
292 #ifdef DEBUG_VALIDATION
293  BUG_ON(my_pool->pending_count);
294 #endif /* DEBUG_VALIDATION */
295  my_pool->pending_pool = NULL;
296  my_pool->pending_head = NULL;
297  my_pool->pending_tail = NULL;
298  }
299 
300  while ((p = PacketPoolGetPacket()) != NULL) {
301  PacketFree(p);
302  }
303 
304 #ifdef DEBUG_VALIDATION
305  my_pool->initialized = 0;
306  my_pool->destroyed = 1;
307 #endif /* DEBUG_VALIDATION */
308 }
309 
311 {
312  return PacketPoolGetPacket();
313 }
314 
316 {
317  bool proot = false;
318 
319  SCEnter();
320  SCLogDebug("Packet %p, p->root %p, alloced %s", p, p->root, BOOL2STR(p->pool == NULL));
321 
322  if (PacketIsTunnel(p)) {
323  SCLogDebug("Packet %p is a tunnel packet: %s",
324  p,p->root ? "upper layer" : "tunnel root");
325 
326  /* get a lock to access root packet fields */
328  SCSpinLock(lock);
329 
330  if (PacketIsTunnelRoot(p)) {
331  SCLogDebug("IS_TUNNEL_ROOT_PKT == TRUE");
332  CaptureStatsUpdate(t, p); // TODO move out of lock
333 
334  const uint16_t outstanding = TUNNEL_PKT_TPR(p) - TUNNEL_PKT_RTV(p);
335  SCLogDebug("root pkt: outstanding %u", outstanding);
336  if (outstanding == 0) {
337  SCLogDebug("no tunnel packets outstanding, no more tunnel "
338  "packet(s) depending on this root");
339  /* if this packet is the root and there are no
340  * more tunnel packets to consider
341  *
342  * return it to the pool */
343  } else {
344  SCLogDebug("tunnel root Packet %p: outstanding > 0, so "
345  "packets are still depending on this root, setting "
346  "SET_TUNNEL_PKT_VERDICTED", p);
347  /* if this is the root and there are more tunnel
348  * packets, return this to the pool. It's still referenced
349  * by the tunnel packets, and we will return it
350  * when we handle them */
351  PacketTunnelSetVerdicted(p);
352 
355  SCReturn;
356  }
357  } else {
358  SCLogDebug("NOT IS_TUNNEL_ROOT_PKT, so tunnel pkt");
359 
361  const uint16_t outstanding = TUNNEL_PKT_TPR(p) - TUNNEL_PKT_RTV(p);
362  SCLogDebug("tunnel pkt: outstanding %u", outstanding);
363  /* all tunnel packets are processed except us. Root already
364  * processed. So return tunnel pkt and root packet to the
365  * pool. */
366  if (outstanding == 0 && p->root && PacketTunnelIsVerdicted(p->root)) {
367  SCLogDebug("root verdicted == true && no outstanding");
368 
369  /* handle freeing the root as well*/
370  SCLogDebug("setting proot = 1 for root pkt, p->root %p "
371  "(tunnel packet %p)", p->root, p);
372  proot = true;
373 
374  /* fall through */
375 
376  } else {
377  /* root not ready yet, or not the last tunnel packet,
378  * so get rid of the tunnel pkt only */
379 
380  SCLogDebug("NOT IS_TUNNEL_PKT_VERDICTED (%s) || "
381  "outstanding > 0 (%u)",
382  (p->root && PacketTunnelIsVerdicted(p->root)) ? "true" : "false",
383  outstanding);
384 
385  /* fall through */
386  }
387  }
389 
390  SCLogDebug("tunnel stuff done, move on (proot %d)", proot);
391 
392  } else {
393  CaptureStatsUpdate(t, p);
394  }
395 
396  SCLogDebug("[packet %p][%s] %s", p,
397  PacketIsTunnel(p) ? PacketIsTunnelRoot(p) ? "tunnel::root" : "tunnel::leaf"
398  : "no tunnel",
399  (p->action & ACTION_DROP) ? "DROP" : "no drop");
400 
401  /* we're done with the tunnel root now as well */
402  if (proot == true) {
403  SCLogDebug("getting rid of root pkt... alloc'd %s", BOOL2STR(p->root->pool == NULL));
404 
406  p->root->ReleasePacket(p->root);
407  p->root = NULL;
408  }
409 
411 
413  p->ReleasePacket(p);
414 
415  SCReturn;
416 }
417 
418 /**
419  * \brief Release all the packets in the queue back to the packetpool. Mainly
420  * used by threads that have failed, and wants to return the packets back
421  * to the packetpool.
422  *
423  * \param pq Pointer to the packetqueue from which the packets have to be
424  * returned back to the packetpool
425  *
426  * \warning this function assumes that the pq does not use locking
427  */
429 {
430  Packet *p = NULL;
431 
432  if (pq == NULL)
433  return;
434 
435  while ((p = PacketDequeue(pq)) != NULL) {
436  DEBUG_VALIDATE_BUG_ON(p->flow != NULL);
437  TmqhOutputPacketpool(NULL, p);
438  }
439 }
440 
441 /** number of packets to keep reserved when calculating the pending
442  * return packets count. This assumes we need at max 10 packets in one
443  * PacketPoolWaitForN call. The actual number is 9 now, so this has a
444  * bit of margin. */
445 #define RESERVED_PACKETS 10
446 
447 /**
448  * \brief Set the max_pending_return_packets value
449  *
450  * Set it to the max pending packets value, divided by the number
451  * of lister threads. Normally, in autofp these are the stream/detect/log
452  * worker threads.
453  *
454  * The max_pending_return_packets value needs to stay below the packet
455  * pool size of the 'producers' (normally pkt capture threads but also
456  * flow timeout injection ) to avoid a deadlock where all the 'workers'
457  * keep packets in their return pools, while the capture thread can't
458  * continue because its pool is empty.
459  */
461 {
462  extern uint16_t max_pending_packets;
463  uint16_t pending_packets = max_pending_packets;
464  if (pending_packets < RESERVED_PACKETS) {
465  FatalError("'max-pending-packets' setting "
466  "must be at least %d",
468  }
470  if (threads == 0)
471  return;
472 
473  uint32_t packets = (pending_packets / threads) - 1;
474  if (packets < max_pending_return_packets)
475  max_pending_return_packets = packets;
476 
477  /* make sure to have a margin in the return logic */
478  if (max_pending_return_packets >= RESERVED_PACKETS)
479  max_pending_return_packets -= RESERVED_PACKETS;
480 
481  SCLogDebug("detect threads %u, max packets %u, max_pending_return_packets %u",
482  threads, packets, max_pending_return_packets);
483 }
tm-threads.h
max_pending_packets
uint16_t max_pending_packets
Definition: suricata.c:181
PktPool_::pending_tail
Packet * pending_tail
Definition: tmqh-packetpool.h:57
PktPool_::cnt
uint32_t cnt
Definition: tmqh-packetpool.h:48
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:314
PacketPoolReturnPacket
void PacketPoolReturnPacket(Packet *p)
Return packet to Packet pool.
Definition: tmqh-packetpool.c:177
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
Packet_::persistent
struct Packet_::@35 persistent
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
PktPool_
Definition: tmqh-packetpool.h:43
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
RESERVED_PACKETS
#define RESERVED_PACKETS
Definition: tmqh-packetpool.c:445
TM_FLAG_DETECT_TM
#define TM_FLAG_DETECT_TM
Definition: tm-modules.h:35
PacketQueue_
simple fifo queue for packets with mutex and cond Calling the mutex or triggering the cond is respons...
Definition: packet-queue.h:49
action-globals.h
Packet_::action
uint8_t action
Definition: decode.h:584
threads.h
Tmqh_::OutHandler
void(* OutHandler)(ThreadVars *, Packet *)
Definition: tm-queuehandlers.h:40
Packet_::pool
struct PktPool_ * pool
Definition: decode.h:645
PacketReleaseRefs
void PacketReleaseRefs(Packet *p)
Definition: packet.c:69
SCCondInit
#define SCCondInit
Definition: threads-debug.h:138
TmThreadCountThreadsByTmmFlags
uint32_t TmThreadCountThreadsByTmmFlags(uint8_t flags)
returns a count of all the threads that match the flag
Definition: tm-threads.c:2015
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
tm-modules.h
SCSpinLock
#define SCSpinLock
Definition: threads-debug.h:235
PktPool_::pending_count
uint32_t pending_count
Definition: tmqh-packetpool.h:58
PacketPoolPostRunmodes
void PacketPoolPostRunmodes(void)
Set the max_pending_return_packets value.
Definition: tmqh-packetpool.c:460
MAX_PENDING_RETURN_PACKETS
#define MAX_PENDING_RETURN_PACKETS
Definition: tmqh-packetpool.c:41
TmqhOutputPacketpool
void TmqhOutputPacketpool(ThreadVars *t, Packet *p)
Definition: tmqh-packetpool.c:315
tmqh-packetpool.h
thread_pkt_pool
thread_local PktPool thread_pkt_pool
Definition: tmqh-packetpool.c:44
PktPool_::pending_head
Packet * pending_head
Definition: tmqh-packetpool.h:56
TmqhReleasePacketsToPacketPool
void TmqhReleasePacketsToPacketPool(PacketQueue *pq)
Release all the packets in the queue back to the packetpool. Mainly used by threads that have failed,...
Definition: tmqh-packetpool.c:428
PacketPoolInit
void PacketPoolInit(void)
Definition: tmqh-packetpool.c:244
Tmqh_::InHandler
Packet *(* InHandler)(ThreadVars *)
Definition: tm-queuehandlers.h:38
lock
HRLOCK_TYPE lock
Definition: host.h:0
decode.h
TUNNEL_PKT_TPR
#define TUNNEL_PKT_TPR(p)
Definition: decode.h:1060
PacketReinit
void PacketReinit(Packet *p)
Recycle a packet structure for reuse.
Definition: packet.c:79
SCCondWait
#define SCCondWait
Definition: threads-debug.h:141
Packet_::tunnel_lock
SCSpinlock tunnel_lock
Definition: decode.h:658
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:119
BOOL2STR
#define BOOL2STR(b)
Definition: util-debug.h:527
CaptureStatsUpdate
void CaptureStatsUpdate(ThreadVars *tv, const Packet *p)
Definition: decode.c:963
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
PacketFree
void PacketFree(Packet *p)
Return a malloced packet.
Definition: decode.c:193
SCSpinUnlock
#define SCSpinUnlock
Definition: threads-debug.h:237
TmqhInputPacketpool
Packet * TmqhInputPacketpool(ThreadVars *tv)
Definition: tmqh-packetpool.c:310
SIZE_OF_PACKET
#define SIZE_OF_PACKET
Definition: decode.h:678
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
util-profiling.h
PacketPoolWait
void PacketPoolWait(void)
Definition: tmqh-packetpool.c:80
SCReturn
#define SCReturn
Definition: util-debug.h:273
Packet_
Definition: decode.h:479
Tmqh_::name
const char * name
Definition: tm-queuehandlers.h:37
SCMutexInit
#define SCMutexInit(mut, mutattrs)
Definition: threads-debug.h:116
PktPool_::return_stack
PktPoolLockedStack return_stack
Definition: tmqh-packetpool.h:72
tm-queuehandlers.h
TmqhPacketpoolRegister
void TmqhPacketpoolRegister(void)
TmqhPacketpoolRegister \initonly.
Definition: tmqh-packetpool.c:55
Packet_::ReleasePacket
void(* ReleasePacket)(struct Packet_ *)
Definition: decode.h:566
SCCondSignal
#define SCCondSignal
Definition: threads-debug.h:139
Packet_::flow
struct Flow_ * flow
Definition: decode.h:518
suricata-common.h
packet.h
ACTION_DROP
#define ACTION_DROP
Definition: action-globals.h:30
FatalError
#define FatalError(...)
Definition: util-debug.h:502
tmqh_table
Tmqh tmqh_table[TMQH_SIZE]
Definition: tm-queuehandlers.c:37
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
PacketDequeue
Packet * PacketDequeue(PacketQueue *q)
Definition: packet-queue.c:216
util-validate.h
PacketGetFromAlloc
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition: decode.c:232
TUNNEL_PKT_RTV
#define TUNNEL_PKT_RTV(p)
Definition: decode.h:1059
Packet_::next
struct Packet_ * next
Definition: decode.h:610
Packet_::root
struct Packet_ * root
Definition: decode.h:628
TUNNEL_INCR_PKT_RTV_NOLOCK
#define TUNNEL_INCR_PKT_RTV_NOLOCK(p)
Definition: decode.h:1047
PACKET_PROFILING_END
#define PACKET_PROFILING_END(p)
Definition: util-profiling.h:86
PacketPoolGetPacket
Packet * PacketPoolGetPacket(void)
Get a new packet from the packet pool.
Definition: tmqh-packetpool.c:127
SCSpinlock
#define SCSpinlock
Definition: threads-debug.h:234
cc_barrier
#define cc_barrier()
Definition: util-optimize.h:43
PktPool_::pending_pool
struct PktPool_ * pending_pool
Definition: tmqh-packetpool.h:55
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
PacketPoolDestroy
void PacketPoolDestroy(void)
Definition: tmqh-packetpool.c:275
PktPool_::head
Packet * head
Definition: tmqh-packetpool.h:47
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
TMQH_PACKETPOOL
@ TMQH_PACKETPOOL
Definition: tm-queuehandlers.h:30