suricata
runmode-pfring.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2018 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 #include "suricata-common.h"
19 #include "tm-threads.h"
20 #include "conf.h"
21 #include "runmodes.h"
22 #include "runmode-pfring.h"
23 #include "source-pfring.h"
24 #include "output.h"
25 
26 #include "util-debug.h"
27 #include "util-time.h"
28 #include "util-cpu.h"
29 #include "util-affinity.h"
30 #include "util-runmodes.h"
31 #include "util-device.h"
32 #include "util-ioctl.h"
33 
34 #ifdef HAVE_PFRING
35 #include <pfring.h>
36 #endif
37 
38 #define PFRING_CONF_V1 1
39 #define PFRING_CONF_V2 2
40 
42 {
43 #ifdef HAVE_PFRING
44  return "workers";
45 #else
46  return NULL;
47 #endif
48 }
49 
51 {
53  "Multi threaded pfring mode. Packets from "
54  "each flow are assigned to a single detect "
55  "thread, unlike \"pfring_auto\" where packets "
56  "from the same flow can be processed by any "
57  "detect thread",
60  "Single threaded pfring mode",
63  "Workers pfring mode, each thread does all"
64  " tasks from acquisition to logging",
66  return;
67 }
68 
69 #ifdef HAVE_PFRING
70 static void PfringDerefConfig(void *conf)
71 {
72  PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
73  if (SC_ATOMIC_SUB(pfp->ref, 1) == 0) {
74  if (pfp->bpf_filter) {
75  SCFree(pfp->bpf_filter);
76  }
77  SCFree(pfp);
78  }
79 }
80 
81 /**
82  * \brief extract information from config file
83  *
84  * The returned structure will be freed by the thread init function.
85  * This is thus necessary to or copy the structure before giving it
86  * to thread or to reparse the file for each thread (and thus have
87  * new structure.
88  *
89  * If old config system is used, then return the smae parameters
90  * value for each interface.
91  *
92  * \return a PfringIfaceConfig corresponding to the interface name
93  */
94 static void *OldParsePfringConfig(const char *iface)
95 {
96  const char *threadsstr = NULL;
97  PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
98  const char *tmpclusterid;
99  const char *tmpctype = NULL;
100  cluster_type default_ctype = CLUSTER_ROUND_ROBIN;
101 
102  if (unlikely(pfconf == NULL)) {
103  return NULL;
104  }
105 
106  if (iface == NULL) {
107  SCFree(pfconf);
108  return NULL;
109  }
110 
111  strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
112  pfconf->flags = 0;
113  pfconf->threads = 1;
114  pfconf->cluster_id = 1;
115  pfconf->ctype = default_ctype;
116  pfconf->DerefFunc = PfringDerefConfig;
118  SC_ATOMIC_INIT(pfconf->ref);
119  (void) SC_ATOMIC_ADD(pfconf->ref, 1);
120 
121  /* Find initial node */
122  if (ConfGet("pfring.threads", &threadsstr) != 1) {
123  pfconf->threads = 1;
124  } else {
125  if (threadsstr != NULL) {
126  pfconf->threads = atoi(threadsstr);
127  }
128  }
129  if (pfconf->threads == 0) {
130  pfconf->threads = 1;
131  }
132 
133  SC_ATOMIC_RESET(pfconf->ref);
134  (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
135 
136  if (strncmp(pfconf->iface, "zc", 2) == 0) {
137  SCLogInfo("ZC interface detected, not setting cluster-id");
138  }
139  else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
140  SCLogInfo("DNA interface detected, not setting cluster-id");
141  } else if (ConfGet("pfring.cluster-id", &tmpclusterid) != 1) {
142  SCLogError(SC_ERR_INVALID_ARGUMENT,"Could not get cluster-id from config");
143  } else {
144  pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
145  pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
146  SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
147  }
148 
149  if (strncmp(pfconf->iface, "zc", 2) == 0) {
150  SCLogInfo("ZC interface detected, not setting cluster type for PF_RING (iface %s)",
151  pfconf->iface);
152  } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
153  SCLogInfo("DNA interface detected, not setting cluster type for PF_RING (iface %s)",
154  pfconf->iface);
155  } else if (ConfGet("pfring.cluster-type", &tmpctype) != 1) {
156  SCLogError(SC_ERR_GET_CLUSTER_TYPE_FAILED,"Could not get cluster-type from config");
157  } else if (strcmp(tmpctype, "cluster_round_robin") == 0) {
158  SCLogInfo("Using round-robin cluster mode for PF_RING (iface %s)",
159  pfconf->iface);
160  pfconf->ctype = (cluster_type)tmpctype;
161  } else if (strcmp(tmpctype, "cluster_flow") == 0) {
162  SCLogInfo("Using flow cluster mode for PF_RING (iface %s)",
163  pfconf->iface);
164  pfconf->ctype = (cluster_type)tmpctype;
165  } else {
166  SCLogError(SC_ERR_INVALID_CLUSTER_TYPE,"invalid cluster-type %s",tmpctype);
167  SCFree(pfconf);
168  return NULL;
169  }
170 
171  return pfconf;
172 }
173 
174 /**
175  * \brief extract information from config file
176  *
177  * The returned structure will be freed by the thread init function.
178  * This is thus necessary to or copy the structure before giving it
179  * to thread or to reparse the file for each thread (and thus have
180  * new structure.
181  *
182  * If old config system is used, then return the smae parameters
183  * value for each interface.
184  *
185  * \return a PfringIfaceConfig corresponding to the interface name
186  */
187 static void *ParsePfringConfig(const char *iface)
188 {
189  const char *threadsstr = NULL;
190  ConfNode *if_root;
191  ConfNode *if_default = NULL;
192  ConfNode *pf_ring_node;
193  PfringIfaceConfig *pfconf = SCMalloc(sizeof(*pfconf));
194  const char *tmpclusterid;
195  const char *tmpctype = NULL;
196  cluster_type default_ctype = CLUSTER_ROUND_ROBIN;
197  int getctype = 0;
198  const char *bpf_filter = NULL;
199  int bool_val;
200 
201  if (unlikely(pfconf == NULL)) {
202  return NULL;
203  }
204 
205  if (iface == NULL) {
206  SCFree(pfconf);
207  return NULL;
208  }
209 
210  memset(pfconf, 0, sizeof(PfringIfaceConfig));
211  strlcpy(pfconf->iface, iface, sizeof(pfconf->iface));
212  pfconf->threads = 1;
213  pfconf->cluster_id = 1;
214  pfconf->ctype = (cluster_type)default_ctype;
215  pfconf->DerefFunc = PfringDerefConfig;
216  SC_ATOMIC_INIT(pfconf->ref);
217  (void) SC_ATOMIC_ADD(pfconf->ref, 1);
218 
219  /* Find initial node */
220  pf_ring_node = ConfGetNode("pfring");
221  if (pf_ring_node == NULL) {
222  SCLogInfo("Unable to find pfring config using default value");
223  return pfconf;
224  }
225 
226  if_root = ConfFindDeviceConfig(pf_ring_node, iface);
227 
228  if_default = ConfFindDeviceConfig(pf_ring_node, "default");
229 
230  if (if_root == NULL && if_default == NULL) {
231  SCLogInfo("Unable to find pfring config for "
232  "interface %s, using default value or 1.0 "
233  "configuration system. ",
234  iface);
235  return pfconf;
236  }
237 
238  /* If there is no setting for current interface use default one as main iface */
239  if (if_root == NULL) {
240  if_root = if_default;
241  if_default = NULL;
242  }
243 
244  if (ConfGetChildValueWithDefault(if_root, if_default, "threads", &threadsstr) != 1) {
245  pfconf->threads = 1;
246  } else if (threadsstr != NULL) {
247  if (strcmp(threadsstr, "auto") == 0) {
248  pfconf->threads = (int)UtilCpuGetNumProcessorsOnline();
249  if (pfconf->threads > 0) {
250  SCLogPerf("%u cores, so using %u threads", pfconf->threads, pfconf->threads);
251  } else {
252  pfconf->threads = GetIfaceRSSQueuesNum(iface);
253  if (pfconf->threads > 0) {
254  SCLogPerf("%d RSS queues, so using %u threads", pfconf->threads, pfconf->threads);
255  }
256  }
257  } else {
258  pfconf->threads = atoi(threadsstr);
259  }
260  }
261  if (pfconf->threads <= 0) {
262  pfconf->threads = 1;
263  }
264 
265  SC_ATOMIC_RESET(pfconf->ref);
266  (void) SC_ATOMIC_ADD(pfconf->ref, pfconf->threads);
267 
268  /* command line value has precedence */
269  if (ConfGet("pfring.cluster-id", &tmpclusterid) == 1) {
270  pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
271  pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
272  SCLogDebug("Going to use command-line provided cluster-id %" PRId32,
273  pfconf->cluster_id);
274  } else {
275 
276  if (strncmp(pfconf->iface, "zc", 2) == 0) {
277  SCLogInfo("ZC interface detected, not setting cluster-id for PF_RING (iface %s)",
278  pfconf->iface);
279  } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
280  SCLogInfo("DNA interface detected, not setting cluster-id for PF_RING (iface %s)",
281  pfconf->iface);
282  } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-id", &tmpclusterid) != 1) {
284  "Could not get cluster-id from config");
285  } else {
286  pfconf->cluster_id = (uint16_t)atoi(tmpclusterid);
287  pfconf->flags |= PFRING_CONF_FLAGS_CLUSTER;
288  SCLogDebug("Going to use cluster-id %" PRId32, pfconf->cluster_id);
289  }
290  }
291 
292  /*load pfring bpf filter*/
293  /* command line value has precedence */
294  if (ConfGet("bpf-filter", &bpf_filter) == 1) {
295  if (strlen(bpf_filter) > 0) {
296  pfconf->bpf_filter = SCStrdup(bpf_filter);
297  if (unlikely(pfconf->bpf_filter == NULL)) {
299  "Can't allocate BPF filter string");
300  } else {
301  SCLogDebug("Going to use command-line provided bpf filter %s",
302  pfconf->bpf_filter);
303  }
304  }
305  } else {
306  if (ConfGetChildValueWithDefault(if_root, if_default, "bpf-filter", &bpf_filter) == 1) {
307  if (strlen(bpf_filter) > 0) {
308  pfconf->bpf_filter = SCStrdup(bpf_filter);
309  if (unlikely(pfconf->bpf_filter == NULL)) {
311  "Can't allocate BPF filter string");
312  } else {
313  SCLogDebug("Going to use bpf filter %s",
314  pfconf->bpf_filter);
315  }
316  }
317  }
318  }
319 
320  if (ConfGet("pfring.cluster-type", &tmpctype) == 1) {
321  SCLogDebug("Going to use command-line provided cluster-type");
322  getctype = 1;
323  } else {
324  if (strncmp(pfconf->iface, "zc", 2) == 0) {
325  SCLogInfo("ZC interface detected, not setting cluster type for PF_RING (iface %s)",
326  pfconf->iface);
327  } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) {
328  SCLogInfo("DNA interface detected, not setting cluster type for PF_RING (iface %s)",
329  pfconf->iface);
330  } else if (ConfGetChildValueWithDefault(if_root, if_default, "cluster-type", &tmpctype) != 1) {
332  "Could not get cluster-type from config");
333  } else {
334  getctype = 1;
335  }
336  }
337 
338  if (getctype) {
339  if (strcmp(tmpctype, "cluster_round_robin") == 0) {
340  SCLogInfo("Using round-robin cluster mode for PF_RING (iface %s)",
341  pfconf->iface);
342  pfconf->ctype = CLUSTER_ROUND_ROBIN;
343  } else if (strcmp(tmpctype, "cluster_flow") == 0) {
344  SCLogInfo("Using flow cluster mode for PF_RING (iface %s)",
345  pfconf->iface);
346  pfconf->ctype = CLUSTER_FLOW;
347  } else {
349  "invalid cluster-type %s",
350  tmpctype);
351  SCFree(pfconf);
352  return NULL;
353  }
354  }
355  if (ConfGetChildValueWithDefault(if_root, if_default, "checksum-checks", &tmpctype) == 1) {
356  if (strcmp(tmpctype, "auto") == 0) {
358  } else if (ConfValIsTrue(tmpctype)) {
360  } else if (ConfValIsFalse(tmpctype)) {
362  } else if (strcmp(tmpctype, "rx-only") == 0) {
364  } else {
365  SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid value for checksum-checks for %s", pfconf->iface);
366  }
367  }
368 
369  if (ConfGetChildValueBoolWithDefault(if_root, if_default, "bypass", &bool_val) == 1) {
370  if (bool_val) {
371 #ifdef HAVE_PF_RING_FLOW_OFFLOAD
372  SCLogConfig("Enabling bypass support in PF_RING for iface %s (if supported by underlying hw)", pfconf->iface);
373  pfconf->flags |= PFRING_CONF_FLAGS_BYPASS;
374 #else
375  SCLogError(SC_ERR_BYPASS_NOT_SUPPORTED, "Bypass is not supported by this Pfring version, please upgrade");
376  SCFree(pfconf);
377  return NULL;
378 #endif
379  }
380  }
381 
382  if (LiveGetOffload() == 0) {
383  if (GetIfaceOffloading(iface, 0, 1) == 1) {
385  "Using PF_RING with offloading activated leads to capture problems");
386  }
387  } else {
389  }
390  return pfconf;
391 }
392 
393 static int PfringConfigGetThreadsCount(void *conf)
394 {
395  PfringIfaceConfig *pfp = (PfringIfaceConfig *)conf;
396  return pfp->threads;
397 }
398 
399 static int PfringConfLevel(void)
400 {
401  const char *def_dev = NULL;
402  /* 1.0 config should return a string */
403  if (ConfGet("pfring.interface", &def_dev) != 1) {
404  return PFRING_CONF_V2;
405  } else {
406  return PFRING_CONF_V1;
407  }
408 }
409 
410 static int GetDevAndParser(const char **live_dev, ConfigIfaceParserFunc *parser)
411 {
412  ConfGet("pfring.live-interface", live_dev);
413 
414  /* determine which config type we have */
415  if (PfringConfLevel() > PFRING_CONF_V1) {
416  *parser = ParsePfringConfig;
417  } else {
418  SCLogInfo("Using 1.0 style configuration for pfring");
419  *parser = OldParsePfringConfig;
420  /* In v1: try to get interface name from config */
421  if (*live_dev == NULL) {
422  if (ConfGet("pfring.interface", live_dev) == 1) {
423  SCLogInfo("Using interface %s", *live_dev);
424  LiveRegisterDevice(*live_dev);
425  } else {
426  SCLogInfo("No interface found, problem incoming");
427  *live_dev = NULL;
428  }
429  }
430  }
431 
432  return 0;
433 }
434 #endif
435 
437 {
438  SCEnter();
439 
440 /* We include only if pfring is enabled */
441 #ifdef HAVE_PFRING
442  int ret;
443  const char *live_dev = NULL;
444  ConfigIfaceParserFunc tparser;
445 
447 
448  TimeModeSetLive();
449 
450  ret = GetDevAndParser(&live_dev, &tparser);
451  if (ret != 0) {
453  "Unable to get parser and interface params");
454  exit(EXIT_FAILURE);
455  }
456 
457  ret = RunModeSetLiveCaptureAutoFp(tparser,
458  PfringConfigGetThreadsCount,
459  "ReceivePfring",
460  "DecodePfring", thread_name_autofp,
461  live_dev);
462  if (ret != 0) {
463  SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
464  exit(EXIT_FAILURE);
465  }
466 
467  SCLogInfo("RunModeIdsPfringAutoFp initialised");
468 #endif /* HAVE_PFRING */
469 
470  return 0;
471 }
472 
474 {
475  SCEnter();
476 
477 /* We include only if pfring is enabled */
478 #ifdef HAVE_PFRING
479  int ret;
480  const char *live_dev = NULL;
481  ConfigIfaceParserFunc tparser;
482 
484 
485  TimeModeSetLive();
486 
487  ret = GetDevAndParser(&live_dev, &tparser);
488  if (ret != 0) {
490  "Unable to get parser and interface params");
491  exit(EXIT_FAILURE);
492  }
493 
494  ret = RunModeSetLiveCaptureSingle(tparser,
495  PfringConfigGetThreadsCount,
496  "ReceivePfring",
497  "DecodePfring", thread_name_single,
498  live_dev);
499  if (ret != 0) {
500  SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
501  exit(EXIT_FAILURE);
502  }
503 
504  SCLogInfo("RunModeIdsPfringSingle initialised");
505 #endif /* HAVE_PFRING */
506 
507  return 0;
508 }
509 
511 {
512  SCEnter();
513 
514 /* We include only if pfring is enabled */
515 #ifdef HAVE_PFRING
516  int ret;
517  const char *live_dev = NULL;
518  ConfigIfaceParserFunc tparser;
519 
521 
522  TimeModeSetLive();
523 
524  ret = GetDevAndParser(&live_dev, &tparser);
525  if (ret != 0) {
527  "Unable to get parser and interface params");
528  exit(EXIT_FAILURE);
529  }
530 
531  ret = RunModeSetLiveCaptureWorkers(tparser,
532  PfringConfigGetThreadsCount,
533  "ReceivePfring",
534  "DecodePfring", thread_name_workers,
535  live_dev);
536  if (ret != 0) {
537  SCLogError(SC_ERR_RUNMODE, "Runmode start failed");
538  exit(EXIT_FAILURE);
539  }
540 
541  SCLogInfo("RunModeIdsPfringWorkers initialised");
542 #endif /* HAVE_PFRING */
543 
544  return 0;
545 }
#define PFRING_CONF_FLAGS_BYPASS
Definition: source-pfring.h:35
#define SCLogDebug(...)
Definition: util-debug.h:335
int RunModeIdsPfringSingle(void)
unsigned int ctype
Definition: source-pfring.h:43
int LiveRegisterDevice(const char *dev)
Add a pcap device for monitoring and create structure.
Definition: util-device.c:124
void RunModeInitialize(void)
Definition: runmodes.c:900
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
#define unlikely(expr)
Definition: util-optimize.h:35
void RunModeIdsPfringRegister(void)
const char * thread_name_single
Definition: runmodes.c:62
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:107
#define SC_ATOMIC_RESET(name)
Initialize the previously declared atomic variable and it&#39;s lock.
Definition: util-atomic.h:90
void TimeModeSetLive(void)
Definition: util-time.c:90
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:124
#define CLUSTER_FLOW
Definition: source-pfring.h:75
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:331
int GetIfaceOffloading(const char *dev, int csum, int other)
output offloading status of the link
Definition: util-ioctl.c:694
void(* DerefFunc)(void *)
Definition: source-pfring.h:53
#define SC_ATOMIC_INIT(name)
Initialize the previously declared atomic variable and it&#39;s lock.
Definition: util-atomic.h:81
ConfNode * ConfFindDeviceConfig(ConfNode *node, const char *iface)
Find the configuration node for a specific device.
Definition: util-conf.c:129
int RunModeSetLiveCaptureAutoFp(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
Definition: util-runmodes.c:89
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define SCEnter(...)
Definition: util-debug.h:337
int ConfValIsFalse(const char *val)
Check if a value is false.
Definition: conf.c:591
void *(* ConfigIfaceParserFunc)(const char *)
Definition: util-runmodes.h:26
LiveDevice * LiveGetDevice(const char *name)
Get a pointer to the device at idx.
Definition: util-device.c:236
#define PFRING_CONF_FLAGS_CLUSTER
Definition: source-pfring.h:34
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:566
int RunModeIdsPfringAutoFp(void)
int RunModeSetLiveCaptureSingle(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
Definition: conf.h:32
#define SCMalloc(a)
Definition: util-mem.h:222
int ConfGetChildValueWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, const char **vptr)
Definition: conf.c:416
ChecksumValidationMode checksum_mode
Definition: source-pfring.h:51
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define SCFree(a)
Definition: util-mem.h:322
int DisableIfaceOffloading(LiveDevice *dev, int csum, int other)
Definition: util-ioctl.c:707
int GetIfaceRSSQueuesNum(const char *pcap_dev)
Definition: util-ioctl.c:737
const char * thread_name_autofp
Definition: runmodes.c:61
#define PFRING_CONF_V1
int RunModeIdsPfringWorkers(void)
#define SCLogPerf(...)
Definition: util-debug.h:261
char iface[PFRING_IFACE_NAME_LENGTH]
Definition: source-pfring.h:45
const char * RunModeIdsPfringGetDefaultMode(void)
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:176
#define PFRING_CONF_V2
#define SCStrdup(a)
Definition: util-mem.h:268
int ConfGetChildValueBoolWithDefault(const ConfNode *base, const ConfNode *dflt, const char *name, int *val)
Definition: conf.c:543
void RunModeRegisterNewRunMode(enum RunModes runmode, const char *name, const char *description, int(*RunModeFunc)(void))
Registers a new runmode.
Definition: runmodes.c:419
#define CLUSTER_ROUND_ROBIN
Definition: source-pfring.h:76
int RunModeSetLiveCaptureWorkers(ConfigIfaceParserFunc ConfigParser, ConfigIfaceThreadsCountFunc ModThreadsCount, const char *recv_mod_name, const char *decode_mod_name, const char *thread_name, const char *live_dev)
uint16_t UtilCpuGetNumProcessorsOnline(void)
Get the number of cpus online in the system.
Definition: util-cpu.c:99
struct SCLogConfig_ SCLogConfig
Holds the config state used by the logging api.
int LiveGetOffload(void)
Definition: util-device.c:79
const char * thread_name_workers
Definition: runmodes.c:63