suricata
util-runmodes.c
Go to the documentation of this file.
1/* Copyright (C) 2011-2019 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 Eric Leblond <eric@regit.org>
22 *
23 * Helper function for runmode.
24 *
25 */
26
27#include "suricata-common.h"
28#include "tm-threads.h"
29#include "conf.h"
30#include "runmodes.h"
31#include "runmode-af-packet.h"
32#include "output.h"
33#include "log-httplog.h"
34
35#include "detect-engine.h"
36#include "detect-engine-mpm.h"
37
38#include "alert-fastlog.h"
39#include "alert-debuglog.h"
40
41#include "util-debug.h"
42#include "util-time.h"
43#include "util-cpu.h"
44#include "util-affinity.h"
45#include "util-device-private.h"
46
47#include "util-runmodes.h"
48
49#include "flow-hash.h"
50
51/** \brief create a queue string for autofp to pass to
52 * the flow queue handler.
53 *
54 * The string will be "pickup1,pickup2,pickup3\0"
55 */
57{
58 if (n > 1024)
59 return NULL;
60
61 /* 13 because pickup12345, = 12 + \0 */
62 size_t queues_size = n * 13;
63 char qname[TM_QUEUE_NAME_MAX];
64
65 char *queues = SCCalloc(1, queues_size);
66 if (unlikely(queues == NULL)) {
67 SCLogError("failed to alloc queues buffer: %s", strerror(errno));
68 return NULL;
69 }
70
71 for (int thread = 0; thread < n; thread++) {
72 if (strlen(queues) > 0)
73 strlcat(queues, ",", queues_size);
74
75 snprintf(qname, sizeof(qname), "pickup%d", (int16_t)thread+1);
76 strlcat(queues, qname, queues_size);
77 }
78
79 SCLogDebug("%d %"PRIuMAX", queues %s", n, (uintmax_t)queues_size, queues);
80 return queues;
81}
82
83/**
84 */
86 ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name,
87 const char *decode_mod_name, const char *thread_name, const char *live_dev)
88{
89 char tname[TM_THREAD_NAME_MAX];
90 char qname[TM_QUEUE_NAME_MAX];
91
92 /* Available cpus */
93 int nlive = LiveGetDeviceCount();
94 uint16_t thread_max = TmThreadsGetWorkerThreadMax();
95
96 char *queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
97 if (queues == NULL) {
98 FatalError("RunmodeAutoFpCreatePickupQueuesString failed");
99 }
100
101 if ((nlive <= 1) && (live_dev != NULL)) {
102 SCLogDebug("live_dev %s", live_dev);
103
104 void *aconf = ConfigParser(live_dev);
105 if (aconf == NULL) {
106 FatalError("Failed to allocate config for %s", live_dev);
107 }
108
109 int threads_count = ModThreadsCount(aconf);
110 SCLogInfo("Going to use %" PRId32 " %s receive thread(s)",
111 threads_count, recv_mod_name);
112
113 /* create the threads */
114 for (int thread = 0; thread < threads_count; thread++) {
115 snprintf(tname, sizeof(tname), "%s#%02d", thread_name, thread+1);
116 ThreadVars *tv_receive =
118 "packetpool", "packetpool",
119 queues, "flow", "pktacqloop");
120 if (tv_receive == NULL) {
121 FatalError("TmThreadsCreate failed");
122 }
123 TmModule *tm_module = TmModuleGetByName(recv_mod_name);
124 if (tm_module == NULL) {
125 FatalError("TmModuleGetByName failed for %s", recv_mod_name);
126 }
127 TmSlotSetFuncAppend(tv_receive, tm_module, aconf);
128
129 tm_module = TmModuleGetByName(decode_mod_name);
130 if (tm_module == NULL) {
131 FatalError("TmModuleGetByName %s failed", decode_mod_name);
132 }
133 TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
134
135 TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
136
137 if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
138 FatalError("TmThreadSpawn failed");
139 }
140 }
141 } else { /* Multiple input device */
142 SCLogInfo("Using %d live device(s).", nlive);
143
144 for (int lthread = 0; lthread < nlive; lthread++) {
145 const char *dev = LiveGetDeviceName(lthread);
146 const char *visual_devname = LiveGetShortName(dev);
147
148 if (dev == NULL) {
149 FatalError("Failed to lookup live dev %d", lthread);
150 }
151 SCLogDebug("dev %s", dev);
152
153 void *aconf = ConfigParser(dev);
154 if (aconf == NULL) {
155 FatalError("Multidev: Failed to allocate config for %s (%d)", dev, lthread);
156 }
157
158 int threads_count = ModThreadsCount(aconf);
159 for (int thread = 0; thread < threads_count; thread++) {
160 char *printable_threadname = SCMalloc(sizeof(char) * (strlen(thread_name)+5+strlen(dev)));
161 if (unlikely(printable_threadname == NULL)) {
162 FatalError("failed to alloc printable thread name: %s", strerror(errno));
163 }
164 snprintf(tname, sizeof(tname), "%s#%02d-%s", thread_name,
165 thread+1, visual_devname);
166 snprintf(printable_threadname, strlen(thread_name)+5+strlen(dev),
167 "%s#%02d-%s", thread_name, thread+1,
168 dev);
169
170 ThreadVars *tv_receive =
172 "packetpool", "packetpool",
173 queues, "flow", "pktacqloop");
174 if (tv_receive == NULL) {
175 FatalError("TmThreadsCreate failed");
176 }
177 tv_receive->printable_name = printable_threadname;
178 tv_receive->iface_name = SCStrdup(dev);
179 if (tv_receive->iface_name == NULL) {
180 FatalError("Failed to allocate memory for iface name");
181 }
182
183 TmModule *tm_module = TmModuleGetByName(recv_mod_name);
184 if (tm_module == NULL) {
185 FatalError("TmModuleGetByName failed for %s", recv_mod_name);
186 }
187 TmSlotSetFuncAppend(tv_receive, tm_module, aconf);
188
189 tm_module = TmModuleGetByName(decode_mod_name);
190 if (tm_module == NULL) {
191 FatalError("TmModuleGetByName %s failed", decode_mod_name);
192 }
193 TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
194
195 TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
196
197 if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
198 FatalError("TmThreadSpawn failed");
199 }
200 }
201 }
202 }
203
204 for (uint16_t thread = 0; thread < thread_max; thread++) {
205 snprintf(tname, sizeof(tname), "%s#%02u", thread_name_workers, (uint16_t)(thread + 1));
206 snprintf(qname, sizeof(qname), "pickup%u", (uint16_t)(thread + 1));
207
208 SCLogDebug("tname %s, qname %s", tname, qname);
209
210 ThreadVars *tv_detect_ncpu =
212 qname, "flow",
213 "packetpool", "packetpool",
214 "varslot");
215 if (tv_detect_ncpu == NULL) {
216 FatalError("TmThreadsCreate failed");
217 }
218 TmModule *tm_module = TmModuleGetByName("FlowWorker");
219 if (tm_module == NULL) {
220 FatalError("TmModuleGetByName for FlowWorker failed");
221 }
222 TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
223
224 TmThreadSetCPU(tv_detect_ncpu, WORKER_CPU_SET);
225
226 TmThreadSetGroupName(tv_detect_ncpu, "Detect");
227
228 tm_module = TmModuleGetByName("RespondReject");
229 if (tm_module == NULL) {
230 FatalError("TmModuleGetByName RespondReject failed");
231 }
232 TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
233
234 if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
235 FatalError("TmThreadSpawn failed");
236 }
237 }
238
239 SCFree(queues);
240 return 0;
241}
242
243/**
244 */
245static int RunModeSetLiveCaptureWorkersForDevice(ConfigIfaceThreadsCountFunc ModThreadsCount,
246 const char *recv_mod_name,
247 const char *decode_mod_name, const char *thread_name,
248 const char *live_dev, void *aconf,
249 unsigned char single_mode)
250{
251 int threads_count;
252 uint16_t thread_max = TmThreadsGetWorkerThreadMax();
253
254 if (single_mode) {
255 threads_count = 1;
256 } else {
257 threads_count = MIN(ModThreadsCount(aconf), thread_max);
258 SCLogInfo("%s: creating %" PRId32 " thread%s", live_dev, threads_count,
259 threads_count > 1 ? "s" : "");
260 }
261
262 /* create the threads */
263 for (int thread = 0; thread < threads_count; thread++) {
264 char tname[TM_THREAD_NAME_MAX];
265 TmModule *tm_module = NULL;
266 const char *visual_devname = LiveGetShortName(live_dev);
267 char *printable_threadname = SCMalloc(sizeof(char) * (strlen(thread_name)+5+strlen(live_dev)));
268 if (unlikely(printable_threadname == NULL)) {
269 FatalError("failed to alloc printable thread name: %s", strerror(errno));
270 exit(EXIT_FAILURE);
271 }
272
273 if (single_mode) {
274 snprintf(tname, sizeof(tname), "%s#01-%s", thread_name, visual_devname);
275 snprintf(printable_threadname, strlen(thread_name)+5+strlen(live_dev), "%s#01-%s",
276 thread_name, live_dev);
277 } else {
278 snprintf(tname, sizeof(tname), "%s#%02d-%s", thread_name,
279 thread+1, visual_devname);
280 snprintf(printable_threadname, strlen(thread_name)+5+strlen(live_dev), "%s#%02d-%s",
281 thread_name, thread+1, live_dev);
282 }
284 "packetpool", "packetpool",
285 "packetpool", "packetpool",
286 "pktacqloop");
287 if (tv == NULL) {
288 FatalError("TmThreadsCreate failed");
289 }
290 tv->printable_name = printable_threadname;
291 tv->iface_name = SCStrdup(live_dev);
292 if (tv->iface_name == NULL) {
293 FatalError("Failed to allocate memory for iface name");
294 }
295
296 tm_module = TmModuleGetByName(recv_mod_name);
297 if (tm_module == NULL) {
298 FatalError("TmModuleGetByName failed for %s", recv_mod_name);
299 }
300 TmSlotSetFuncAppend(tv, tm_module, aconf);
301
302 tm_module = TmModuleGetByName(decode_mod_name);
303 if (tm_module == NULL) {
304 FatalError("TmModuleGetByName %s failed", decode_mod_name);
305 }
306 TmSlotSetFuncAppend(tv, tm_module, NULL);
307
308 tm_module = TmModuleGetByName("FlowWorker");
309 if (tm_module == NULL) {
310 FatalError("TmModuleGetByName for FlowWorker failed");
311 }
312 TmSlotSetFuncAppend(tv, tm_module, NULL);
313
314 tm_module = TmModuleGetByName("RespondReject");
315 if (tm_module == NULL) {
316 FatalError("TmModuleGetByName RespondReject failed");
317 }
318 TmSlotSetFuncAppend(tv, tm_module, NULL);
319
321
322 if (TmThreadSpawn(tv) != TM_ECODE_OK) {
323 FatalError("TmThreadSpawn failed");
324 }
325 }
326
327 return 0;
328}
329
331 ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name,
332 const char *decode_mod_name, const char *thread_name, const char *live_dev)
333{
334 int nlive = LiveGetDeviceCount();
335 void *aconf;
336 int ldev;
337
338 for (ldev = 0; ldev < nlive; ldev++) {
339 const char *live_dev_c = NULL;
340 if ((nlive <= 1) && (live_dev != NULL)) {
341 aconf = ConfigParser(live_dev);
342 live_dev_c = live_dev;
343 } else {
344 live_dev_c = LiveGetDeviceName(ldev);
345 aconf = ConfigParser(live_dev_c);
346 }
347 RunModeSetLiveCaptureWorkersForDevice(ModThreadsCount,
348 recv_mod_name,
349 decode_mod_name,
350 thread_name,
351 live_dev_c,
352 aconf,
353 0);
354 }
355
356 return 0;
357}
358
360 ConfigIfaceThreadsCountFunc ModThreadsCount,
361 const char *recv_mod_name,
362 const char *decode_mod_name, const char *thread_name,
363 const char *live_dev)
364{
365 int nlive = LiveGetDeviceCount();
366 const char *live_dev_c = NULL;
367 void *aconf;
368
369 if (nlive > 1) {
370 FatalError("Can't use the 'single' runmode with multiple devices");
371 }
372
373 if (live_dev != NULL) {
374 aconf = ConfigParser(live_dev);
375 live_dev_c = live_dev;
376 } else {
377 live_dev_c = LiveGetDeviceName(0);
378 aconf = ConfigParser(live_dev_c);
379 }
380
381 return RunModeSetLiveCaptureWorkersForDevice(
382 ModThreadsCount,
383 recv_mod_name,
384 decode_mod_name,
385 thread_name,
386 live_dev_c,
387 aconf,
388 1);
389}
390
391
392/**
393 */
395 const char *recv_mod_name,
396 const char *verdict_mod_name,
397 const char *decode_mod_name)
398{
399 SCEnter();
400 char tname[TM_THREAD_NAME_MAX];
401 TmModule *tm_module ;
402
403 /* Available cpus */
404 const int nqueue = LiveGetDeviceCount();
405
406 uint16_t thread_max = TmThreadsGetWorkerThreadMax();
407
408 char *queues = RunmodeAutoFpCreatePickupQueuesString(thread_max);
409 if (queues == NULL) {
410 FatalError("RunmodeAutoFpCreatePickupQueuesString failed");
411 }
412
413 /* create the threads */
414 for (int i = 0; i < nqueue; i++) {
415 const char *cur_queue = LiveGetDeviceName(i);
416 if (cur_queue == NULL) {
417 FatalError("invalid queue number");
418 }
419 memset(tname, 0, sizeof(tname));
420 snprintf(tname, sizeof(tname), "%s-%s", thread_name_autofp, cur_queue);
421
422 ThreadVars *tv_receive =
424 "packetpool", "packetpool",
425 queues, "flow", "pktacqloop");
426 if (tv_receive == NULL) {
427 FatalError("TmThreadsCreate failed");
428 }
429 tm_module = TmModuleGetByName(recv_mod_name);
430 if (tm_module == NULL) {
431 FatalError("TmModuleGetByName failed for %s", recv_mod_name);
432 }
433 TmSlotSetFuncAppend(tv_receive, tm_module, (void *) ConfigParser(i));
434
435 tm_module = TmModuleGetByName(decode_mod_name);
436 if (tm_module == NULL) {
437 FatalError("TmModuleGetByName %s failed", decode_mod_name);
438 }
439 TmSlotSetFuncAppend(tv_receive, tm_module, NULL);
440
441 TmThreadSetCPU(tv_receive, RECEIVE_CPU_SET);
442
443 if (TmThreadSpawn(tv_receive) != TM_ECODE_OK) {
444 FatalError("TmThreadSpawn failed");
445 }
446
447 }
448 for (int thread = 0; thread < thread_max; thread++) {
449 snprintf(tname, sizeof(tname), "%s#%02u", thread_name_workers, (uint16_t)(thread + 1));
450 char qname[TM_QUEUE_NAME_MAX];
451 snprintf(qname, sizeof(qname), "pickup%u", (uint16_t)(thread + 1));
452
453 SCLogDebug("tname %s, qname %s", tname, qname);
454
455 ThreadVars *tv_detect_ncpu =
457 qname, "flow",
458 "verdict-queue", "simple",
459 "varslot");
460 if (tv_detect_ncpu == NULL) {
461 FatalError("TmThreadsCreate failed");
462 }
463
464 tm_module = TmModuleGetByName("FlowWorker");
465 if (tm_module == NULL) {
466 FatalError("TmModuleGetByName for FlowWorker failed");
467 }
468 TmSlotSetFuncAppend(tv_detect_ncpu, tm_module, NULL);
469
470 TmThreadSetCPU(tv_detect_ncpu, WORKER_CPU_SET);
471
472 TmThreadSetGroupName(tv_detect_ncpu, "Detect");
473
474 if (TmThreadSpawn(tv_detect_ncpu) != TM_ECODE_OK) {
475 FatalError("TmThreadSpawn failed");
476 }
477 }
478
479 /* create the threads */
480 for (int i = 0; i < nqueue; i++) {
481 memset(tname, 0, sizeof(tname));
482 snprintf(tname, sizeof(tname), "%s#%02d", thread_name_verdict, i);
483
484 ThreadVars *tv_verdict =
486 "verdict-queue", "simple",
487 "packetpool", "packetpool",
488 "varslot");
489 if (tv_verdict == NULL) {
490 FatalError("TmThreadsCreate failed");
491 }
492 tm_module = TmModuleGetByName(verdict_mod_name);
493 if (tm_module == NULL) {
494 FatalError("TmModuleGetByName %s failed", verdict_mod_name);
495 }
496 TmSlotSetFuncAppend(tv_verdict, tm_module, (void *)ConfigParser(i));
497
498 tm_module = TmModuleGetByName("RespondReject");
499 if (tm_module == NULL) {
500 FatalError("TmModuleGetByName for RespondReject failed");
501 }
502 TmSlotSetFuncAppend(tv_verdict, tm_module, NULL);
503
504 TmThreadSetCPU(tv_verdict, VERDICT_CPU_SET);
505
506 if (TmThreadSpawn(tv_verdict) != TM_ECODE_OK) {
507 FatalError("TmThreadSpawn failed");
508 }
509 }
510
511 SCFree(queues);
512 return 0;
513}
514
515/**
516 */
518 const char *recv_mod_name,
519 const char *verdict_mod_name,
520 const char *decode_mod_name)
521{
522 TmModule *tm_module = NULL;
523 const int nqueue = LiveGetDeviceCount();
524
525 for (int i = 0; i < nqueue; i++) {
526 /* create the threads */
527 const char *cur_queue = LiveGetDeviceName(i);
528 if (cur_queue == NULL) {
529 FatalError("invalid queue number");
530 }
531
532 char tname[TM_THREAD_NAME_MAX];
533 memset(tname, 0, sizeof(tname));
534 snprintf(tname, sizeof(tname), "%s-%s", thread_name_workers, cur_queue);
535
537 "packetpool", "packetpool",
538 "packetpool", "packetpool",
539 "pktacqloop");
540 if (tv == NULL) {
541 FatalError("TmThreadsCreate failed");
542 }
543
544 tm_module = TmModuleGetByName(recv_mod_name);
545 if (tm_module == NULL) {
546 FatalError("TmModuleGetByName failed for %s", recv_mod_name);
547 }
548 TmSlotSetFuncAppend(tv, tm_module, (void *) ConfigParser(i));
549
550 tm_module = TmModuleGetByName(decode_mod_name);
551 if (tm_module == NULL) {
552 FatalError("TmModuleGetByName %s failed", decode_mod_name);
553 }
554 TmSlotSetFuncAppend(tv, tm_module, NULL);
555
556 tm_module = TmModuleGetByName("FlowWorker");
557 if (tm_module == NULL) {
558 FatalError("TmModuleGetByName for FlowWorker failed");
559 }
560 TmSlotSetFuncAppend(tv, tm_module, NULL);
561
562 tm_module = TmModuleGetByName(verdict_mod_name);
563 if (tm_module == NULL) {
564 FatalError("TmModuleGetByName %s failed", verdict_mod_name);
565 }
566 TmSlotSetFuncAppend(tv, tm_module, (void *) ConfigParser(i));
567
568 tm_module = TmModuleGetByName("RespondReject");
569 if (tm_module == NULL) {
570 FatalError("TmModuleGetByName for RespondReject failed");
571 }
572 TmSlotSetFuncAppend(tv, tm_module, NULL);
573
575
576 if (TmThreadSpawn(tv) != TM_ECODE_OK) {
577 FatalError("TmThreadSpawn failed");
578 }
579 }
580
581 return 0;
582}
ThreadVars * tv
const char * thread_name_verdict
Definition runmodes.c:69
const char * thread_name_autofp
Definition runmodes.c:66
const char * thread_name_workers
Definition runmodes.c:68
Per thread variable structure.
Definition threadvars.h:58
char * printable_name
Definition threadvars.h:66
char * iface_name
Definition threadvars.h:139
#define MIN(x, y)
size_t strlcat(char *, const char *src, size_t siz)
TmModule * TmModuleGetByName(const char *name)
get a tm module ptr by name
Definition tm-modules.c:46
@ TM_ECODE_OK
void TmSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, const void *data)
Appends a new entry to the slots.
Definition tm-threads.c:658
void TmThreadSetGroupName(ThreadVars *tv, const char *name)
ThreadVars * TmThreadCreatePacketHandler(const char *name, const char *inq_name, const char *inqh_name, const char *outq_name, const char *outqh_name, const char *slots)
Creates and returns a TV instance for a Packet Processing Thread. This function doesn't support custo...
uint16_t TmThreadsGetWorkerThreadMax(void)
TmEcode TmThreadSetCPU(ThreadVars *tv, uint8_t type)
Definition tm-threads.c:831
TmEcode TmThreadSpawn(ThreadVars *tv)
Spawns a thread associated with the ThreadVars instance tv.
#define TM_QUEUE_NAME_MAX
Definition tm-threads.h:48
#define TM_THREAD_NAME_MAX
Definition tm-threads.h:49
@ RECEIVE_CPU_SET
@ VERDICT_CPU_SET
@ WORKER_CPU_SET
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
const char * LiveGetShortName(const char *dev)
int LiveGetDeviceCount(void)
Get the number of registered devices.
const char * LiveGetDeviceName(int number)
Get a pointer to the device name at idx.
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define SCStrdup(s)
Definition util-mem.h:56
#define unlikely(expr)
int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
int RunModeSetIPSWorker(ConfigIPSParserFunc ConfigParser, const char *recv_mod_name, const char *verdict_mod_name, const char *decode_mod_name)
int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
char * RunmodeAutoFpCreatePickupQueuesString(int n)
create a queue string for autofp to pass to the flow queue handler.
int RunModeSetIPSAutoFp(ConfigIPSParserFunc ConfigParser, const char *recv_mod_name, const char *verdict_mod_name, const char *decode_mod_name)
int RunModeSetLiveCaptureAutoFp(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
int(* ConfigIfaceThreadsCountFunc)(void *)
void *(* ConfigIfaceParserFunc)(const char *)
void *(* ConfigIPSParserFunc)(int)