Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-device.c
Line
Count
Source
1
/* Copyright (C) 2011-2021 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 "conf.h"
20
#include "util-device.h"
21
#include "util-ioctl.h"
22
#include "util-misc.h"
23
#include "util-dpdk.h"
24
25
#include "device-storage.h"
26
#include "util-debug.h"
27
28
0
#define MAX_DEVNAME 10
29
30
static LiveDevStorageId g_bypass_storage_id = { .id = -1 };
31
32
/**
33
 * \file
34
 *
35
 * \author Eric Leblond <eric@regit.org>
36
 *
37
 *  \brief Utility functions to handle device list
38
 */
39
40
/** private device list */
41
static TAILQ_HEAD(, LiveDevice_) live_devices =
42
    TAILQ_HEAD_INITIALIZER(live_devices);
43
44
/** List of the name of devices
45
 *
46
 * As we don't know the size of the Storage on devices
47
 * before the parsing we need to wait and use this list
48
 * to create later the LiveDevice via LiveDeviceFinalize()
49
 */
50
static TAILQ_HEAD(, LiveDeviceName_) pre_live_devices =
51
    TAILQ_HEAD_INITIALIZER(pre_live_devices);
52
53
typedef struct BypassInfo_ {
54
    SC_ATOMIC_DECLARE(uint64_t, ipv4_hash_count);
55
    SC_ATOMIC_DECLARE(uint64_t, ipv4_fail);
56
    SC_ATOMIC_DECLARE(uint64_t, ipv4_success);
57
    SC_ATOMIC_DECLARE(uint64_t, ipv6_hash_count);
58
    SC_ATOMIC_DECLARE(uint64_t, ipv6_fail);
59
    SC_ATOMIC_DECLARE(uint64_t, ipv6_success);
60
} BypassInfo;
61
62
/** if set to 0 when we don't have real devices */
63
static int live_devices_stats = 1;
64
65
66
static int LiveSafeDeviceName(const char *devname,
67
                              char *newdevname, size_t destlen);
68
69
static int g_live_devices_disable_offloading = 1;
70
71
void LiveSetOffloadDisable(void)
72
71
{
73
71
    g_live_devices_disable_offloading = 1;
74
71
}
75
76
void LiveSetOffloadWarn(void)
77
0
{
78
0
    g_live_devices_disable_offloading = 0;
79
0
}
80
81
int LiveGetOffload(void)
82
0
{
83
0
    return g_live_devices_disable_offloading;
84
0
}
85
86
/**
87
 *  \brief Add a device for monitoring
88
 *
89
 * To be used during option parsing. When a device has
90
 * to be created during runmode init, use LiveRegisterDevice()
91
 *
92
 *  \param dev string with the device name
93
 *
94
 *  \retval 0 on success.
95
 *  \retval -1 on failure.
96
 */
97
int LiveRegisterDeviceName(const char *dev)
98
0
{
99
0
    LiveDeviceName *pd = NULL;
100
101
0
    pd = SCCalloc(1, sizeof(LiveDeviceName));
102
0
    if (unlikely(pd == NULL)) {
103
0
        return -1;
104
0
    }
105
106
0
    pd->dev = SCStrdup(dev);
107
0
    if (unlikely(pd->dev == NULL)) {
108
0
        SCFree(pd);
109
0
        return -1;
110
0
    }
111
112
0
    TAILQ_INSERT_TAIL(&pre_live_devices, pd, next);
113
114
0
    SCLogDebug("Device \"%s\" registered.", dev);
115
0
    return 0;
116
0
}
117
118
/**
119
 *  \brief Add a pcap device for monitoring and create structure
120
 *
121
 *  \param dev string with the device name
122
 *
123
 *  \retval 0 on success.
124
 *  \retval -1 on failure.
125
 */
126
int LiveRegisterDevice(const char *dev)
127
0
{
128
0
    LiveDevice *pd = NULL;
129
130
0
    pd = SCCalloc(1, sizeof(LiveDevice) + LiveDevStorageSize());
131
0
    if (unlikely(pd == NULL)) {
132
0
        return -1;
133
0
    }
134
135
0
    int id = LiveGetDeviceCount();
136
0
    if (id > UINT16_MAX) {
137
0
        SCFree(pd);
138
0
        return -1;
139
0
    }
140
141
0
    pd->dev = SCStrdup(dev);
142
0
    if (unlikely(pd->dev == NULL)) {
143
0
        SCFree(pd);
144
0
        return -1;
145
0
    }
146
    /* create a short version to be used in thread names */
147
0
    LiveSafeDeviceName(pd->dev, pd->dev_short, sizeof(pd->dev_short));
148
149
0
    SC_ATOMIC_INIT(pd->pkts);
150
0
    SC_ATOMIC_INIT(pd->drop);
151
0
    SC_ATOMIC_INIT(pd->invalid_checksums);
152
0
    pd->id = (uint16_t)id;
153
0
    TAILQ_INSERT_TAIL(&live_devices, pd, next);
154
155
0
    SCLogDebug("Device \"%s\" registered and created.", dev);
156
0
    return 0;
157
0
}
158
159
/**
160
 *  \brief Get the number of registered devices
161
 *
162
 *  \retval cnt the number of registered devices
163
 */
164
int LiveGetDeviceCount(void)
165
0
{
166
0
    int i = 0;
167
0
    LiveDevice *pd;
168
169
0
    TAILQ_FOREACH(pd, &live_devices, next) {
170
0
        i++;
171
0
    }
172
173
0
    return i;
174
0
}
175
176
/**
177
 *  \brief Get a pointer to the device name at idx
178
 *
179
 *  \param number idx of the device in our list
180
 *
181
 *  \retval ptr pointer to the string containing the device
182
 *  \retval NULL on error
183
 */
184
const char *LiveGetDeviceName(int number)
185
0
{
186
0
    int i = 0;
187
0
    LiveDevice *pd;
188
189
0
    TAILQ_FOREACH(pd, &live_devices, next) {
190
0
        if (i == number) {
191
0
            return pd->dev;
192
0
        }
193
194
0
        i++;
195
0
    }
196
197
0
    return NULL;
198
0
}
199
200
/** \internal
201
 *  \brief Shorten a device name that is to long
202
 *
203
 *  \param device name from config and destination for modified
204
 *
205
 *  \retval None, is added to destination char *newdevname
206
 */
207
static int LiveSafeDeviceName(const char *devname, char *newdevname, size_t destlen)
208
0
{
209
0
    const size_t devnamelen = strlen(devname);
210
211
    /* If we have to shorten the interface name */
212
0
    if (devnamelen > MAX_DEVNAME) {
213
        /* special mode for DPDK pci addresses */
214
0
        if (devnamelen >= 5 && strncmp(devname, "0000:", 5) == 0) {
215
0
            strlcpy(newdevname, devname + 5, destlen);
216
0
            return 0;
217
0
        }
218
219
        /* IF the dest length is over 10 chars long it will not do any
220
         * good for the shortening. The shortening is done due to the
221
         * max length of pthread names (15 chars) and we use 3 chars
222
         * for the threadname indicator eg. "W#-" and one-two chars for
223
         * the thread number. And if the destination buffer is under
224
         * 6 chars there is no point in shortening it since we must at
225
         * least enter two periods (.) into the string.
226
         */
227
0
        if ((destlen-1) > 10 || (destlen-1) < 6) {
228
0
            return 1;
229
0
        }
230
231
0
        ShortenString(devname, newdevname, destlen, '.');
232
233
0
        SCLogInfo("%s: shortening device name to %s", devname, newdevname);
234
0
    } else {
235
0
        strlcpy(newdevname, devname, destlen);
236
0
    }
237
0
    return 0;
238
0
}
239
240
/**
241
 *  \brief Get a pointer to the device at idx
242
 *
243
 *  \param number idx of the device in our list
244
 *
245
 *  \retval ptr pointer to the string containing the device
246
 *  \retval NULL on error
247
 */
248
LiveDevice *LiveGetDevice(const char *name)
249
0
{
250
0
    LiveDevice *pd;
251
252
0
    if (name == NULL) {
253
0
        SCLogWarning("Name of device should not be null");
254
0
        return NULL;
255
0
    }
256
257
0
    TAILQ_FOREACH(pd, &live_devices, next) {
258
0
        if (!strcmp(name, pd->dev)) {
259
0
            return pd;
260
0
        }
261
0
    }
262
263
0
    return NULL;
264
0
}
265
266
const char *LiveGetShortName(const char *dev)
267
0
{
268
0
    LiveDevice *live_dev = LiveGetDevice(dev);
269
0
    if (live_dev == NULL)
270
0
        return NULL;
271
0
    return live_dev->dev_short;
272
0
}
273
274
int LiveBuildDeviceList(const char *runmode)
275
0
{
276
0
    return LiveBuildDeviceListCustom(runmode, "interface");
277
0
}
278
279
int LiveBuildDeviceListCustom(const char *runmode, const char *itemname)
280
0
{
281
0
    ConfNode *base = ConfGetNode(runmode);
282
0
    ConfNode *child;
283
0
    int i = 0;
284
285
0
    if (base == NULL)
286
0
        return 0;
287
288
0
    TAILQ_FOREACH(child, &base->head, next) {
289
0
        ConfNode *subchild;
290
0
        TAILQ_FOREACH(subchild, &child->head, next) {
291
0
            if ((!strcmp(subchild->name, itemname))) {
292
0
                if (!strcmp(subchild->val, "default"))
293
0
                    break;
294
0
                SCLogConfig("Adding %s %s from config file",
295
0
                          itemname, subchild->val);
296
0
                LiveRegisterDeviceName(subchild->val);
297
0
                i++;
298
0
            }
299
0
        }
300
0
    }
301
302
0
    return i;
303
0
}
304
305
/** Call this function to disable stat on live devices
306
 *
307
 * This can be useful in the case, this is not a real interface.
308
 */
309
void LiveDeviceHasNoStats(void)
310
0
{
311
0
    live_devices_stats = 0;
312
0
}
313
314
int LiveDeviceListClean(void)
315
0
{
316
0
    SCEnter();
317
0
    LiveDevice *pd, *tpd;
318
319
    /* dpdk: need to close all devices before freeing them. */
320
0
    TAILQ_FOREACH (pd, &live_devices, next) {
321
0
        DPDKCloseDevice(pd);
322
0
    }
323
0
    TAILQ_FOREACH_SAFE(pd, &live_devices, next, tpd) {
324
0
        if (live_devices_stats) {
325
0
            SCLogNotice("%s: packets: %" PRIu64 ", drops: %" PRIu64
326
0
                        " (%.2f%%), invalid chksum: %" PRIu64,
327
0
                    pd->dev, SC_ATOMIC_GET(pd->pkts), SC_ATOMIC_GET(pd->drop),
328
0
                    SC_ATOMIC_GET(pd->pkts) > 0 ? 100 * ((double)SC_ATOMIC_GET(pd->drop)) /
329
0
                                                          (double)SC_ATOMIC_GET(pd->pkts)
330
0
                                                : 0,
331
0
                    SC_ATOMIC_GET(pd->invalid_checksums));
332
0
        }
333
334
0
        RestoreIfaceOffloading(pd);
335
0
        DPDKFreeDevice(pd);
336
337
0
        if (pd->dev)
338
0
            SCFree(pd->dev);
339
0
        LiveDevFreeStorage(pd);
340
0
        SCFree(pd);
341
0
    }
342
343
0
    SCReturnInt(TM_ECODE_OK);
344
0
}
345
346
#ifdef BUILD_UNIX_SOCKET
347
TmEcode LiveDeviceIfaceStat(json_t *cmd, json_t *answer, void *data)
348
0
{
349
0
    SCEnter();
350
0
    LiveDevice *pd;
351
0
    const char * name = NULL;
352
0
    json_t *jarg = json_object_get(cmd, "iface");
353
0
    if(!json_is_string(jarg)) {
354
0
        json_object_set_new(answer, "message", json_string("Iface is not a string"));
355
0
        SCReturnInt(TM_ECODE_FAILED);
356
0
    }
357
0
    name = json_string_value(jarg);
358
0
    if (name == NULL) {
359
0
        json_object_set_new(answer, "message", json_string("Iface name is NULL"));
360
0
        SCReturnInt(TM_ECODE_FAILED);
361
0
    }
362
363
0
    TAILQ_FOREACH(pd, &live_devices, next) {
364
0
        if (!strcmp(name, pd->dev)) {
365
0
            json_t *jdata = json_object();
366
0
            if (jdata == NULL) {
367
0
                json_object_set_new(answer, "message",
368
0
                        json_string("internal error at json object creation"));
369
0
                SCReturnInt(TM_ECODE_FAILED);
370
0
            }
371
0
            json_object_set_new(jdata, "pkts",
372
0
                                json_integer(SC_ATOMIC_GET(pd->pkts)));
373
0
            json_object_set_new(jdata, "invalid-checksums",
374
0
                                json_integer(SC_ATOMIC_GET(pd->invalid_checksums)));
375
0
            json_object_set_new(jdata, "drop",
376
0
                                json_integer(SC_ATOMIC_GET(pd->drop)));
377
0
            json_object_set_new(jdata, "bypassed",
378
0
                                json_integer(SC_ATOMIC_GET(pd->bypassed)));
379
0
            json_object_set_new(answer, "message", jdata);
380
0
            SCReturnInt(TM_ECODE_OK);
381
0
        }
382
0
    }
383
0
    json_object_set_new(answer, "message", json_string("Iface does not exist"));
384
0
    SCReturnInt(TM_ECODE_FAILED);
385
0
}
386
387
TmEcode LiveDeviceIfaceList(json_t *cmd, json_t *answer, void *data)
388
0
{
389
0
    SCEnter();
390
0
    json_t *jdata;
391
0
    json_t *jarray;
392
0
    LiveDevice *pd;
393
0
    int i = 0;
394
395
0
    jdata = json_object();
396
0
    if (jdata == NULL) {
397
0
        json_object_set_new(answer, "message",
398
0
                            json_string("internal error at json object creation"));
399
0
        return TM_ECODE_FAILED;
400
0
    }
401
0
    jarray = json_array();
402
0
    if (jarray == NULL) {
403
0
        json_object_set_new(answer, "message",
404
0
                            json_string("internal error at json object creation"));
405
0
        return TM_ECODE_FAILED;
406
0
    }
407
0
    TAILQ_FOREACH(pd, &live_devices, next) {
408
0
        json_array_append_new(jarray, json_string(pd->dev));
409
0
        i++;
410
0
    }
411
412
0
    json_object_set_new(jdata, "count", json_integer(i));
413
0
    json_object_set_new(jdata, "ifaces", jarray);
414
0
    json_object_set_new(answer, "message", jdata);
415
0
    SCReturnInt(TM_ECODE_OK);
416
0
}
417
418
#endif /* BUILD_UNIX_SOCKET */
419
420
LiveDevice *LiveDeviceForEach(LiveDevice **ldev, LiveDevice **ndev)
421
0
{
422
0
    if (*ldev == NULL) {
423
0
        *ldev = TAILQ_FIRST(&live_devices);
424
0
        *ndev = TAILQ_NEXT(*ldev, next);
425
0
        return *ldev;
426
0
    } else {
427
0
        *ldev = *ndev;
428
0
        if (*ldev) {
429
0
            *ndev = TAILQ_NEXT(*ldev, next);
430
0
        }
431
0
        return *ldev;
432
0
    }
433
0
    return NULL;
434
0
}
435
436
/**
437
 * Create registered devices
438
 *
439
 * This function creates all needed LiveDevice from
440
 * the LiveDeviceName list created via LiveRegisterDevice()
441
 */
442
void LiveDeviceFinalize(void)
443
71
{
444
71
    LiveDeviceName *ld, *pld;
445
71
    SCLogDebug("Finalize live device");
446
    /* Iter on devices and register them */
447
71
    TAILQ_FOREACH_SAFE(ld, &pre_live_devices, next, pld) {
448
0
        if (ld->dev) {
449
0
            LiveRegisterDevice(ld->dev);
450
0
            SCFree(ld->dev);
451
0
        }
452
0
        SCFree(ld);
453
0
    }
454
71
}
455
456
static void LiveDevExtensionFree(void *x)
457
0
{
458
0
    if (x)
459
0
        SCFree(x);
460
0
}
461
462
/**
463
 * Register bypass stats storage
464
 */
465
void LiveDevRegisterExtension(void)
466
0
{
467
0
    g_bypass_storage_id = LiveDevStorageRegister("bypass_stats", sizeof(void *),
468
0
                                                 NULL, LiveDevExtensionFree);
469
0
}
470
471
/**
472
 * Prepare a LiveDevice so we can set bypass stats
473
 */
474
int LiveDevUseBypass(LiveDevice *dev)
475
0
{
476
0
    BypassInfo *bpinfo = SCCalloc(1, sizeof(*bpinfo));
477
0
    if (bpinfo == NULL) {
478
0
        SCLogError("Can't allocate bypass info structure");
479
0
        return -1;
480
0
    }
481
482
0
    SC_ATOMIC_INIT(bpinfo->ipv4_hash_count);
483
0
    SC_ATOMIC_INIT(bpinfo->ipv4_hash_count);
484
485
0
    LiveDevSetStorageById(dev, g_bypass_storage_id, bpinfo);
486
0
    return 0;
487
0
}
488
489
/**
490
 * Set number of currently bypassed flows for a protocol family
491
 *
492
 * \param dev pointer to LiveDevice to set stats for
493
 * \param cnt number of currently bypassed flows
494
 * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count
495
 */
496
void LiveDevSetBypassStats(LiveDevice *dev, uint64_t cnt, int family)
497
0
{
498
0
    BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id);
499
0
    if (bpfdata) {
500
0
        if (family == AF_INET) {
501
0
            SC_ATOMIC_SET(bpfdata->ipv4_hash_count, cnt);
502
0
        } else if (family == AF_INET6) {
503
0
            SC_ATOMIC_SET(bpfdata->ipv6_hash_count, cnt);
504
0
        }
505
0
    }
506
0
}
507
508
/**
509
 * Increase number of currently bypassed flows for a protocol family
510
 *
511
 * \param dev pointer to LiveDevice to set stats for
512
 * \param cnt number of flows to add
513
 * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count
514
 */
515
void LiveDevAddBypassStats(LiveDevice *dev, uint64_t cnt, int family)
516
0
{
517
0
    BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id);
518
0
    if (bpfdata) {
519
0
        if (family == AF_INET) {
520
0
            SC_ATOMIC_ADD(bpfdata->ipv4_hash_count, cnt);
521
0
        } else if (family == AF_INET6) {
522
0
            SC_ATOMIC_ADD(bpfdata->ipv6_hash_count, cnt);
523
0
        }
524
0
    }
525
0
}
526
527
/**
528
 * Decrease number of currently bypassed flows for a protocol family
529
 *
530
 * \param dev pointer to LiveDevice to set stats for
531
 * \param cnt number of flows to remove
532
 * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count
533
 */
534
void LiveDevSubBypassStats(LiveDevice *dev, uint64_t cnt, int family)
535
0
{
536
0
    BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id);
537
0
    if (bpfdata) {
538
0
        if (family == AF_INET) {
539
0
            SC_ATOMIC_SUB(bpfdata->ipv4_hash_count, cnt);
540
0
        } else if (family == AF_INET6) {
541
0
            SC_ATOMIC_SUB(bpfdata->ipv6_hash_count, cnt);
542
0
        }
543
0
    }
544
0
}
545
546
/**
547
 * Increase number of failed captured flows for a protocol family
548
 *
549
 * \param dev pointer to LiveDevice to set stats for
550
 * \param cnt number of flows to add
551
 * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count
552
 */
553
void LiveDevAddBypassFail(LiveDevice *dev, uint64_t cnt, int family)
554
0
{
555
0
    BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id);
556
0
    if (bpfdata) {
557
0
        if (family == AF_INET) {
558
0
            SC_ATOMIC_ADD(bpfdata->ipv4_fail, cnt);
559
0
        } else if (family == AF_INET6) {
560
0
            SC_ATOMIC_ADD(bpfdata->ipv6_fail, cnt);
561
0
        }
562
0
    }
563
0
}
564
565
/**
566
 * Increase number of currently successfully bypassed flows for a protocol family
567
 *
568
 * \param dev pointer to LiveDevice to set stats for
569
 * \param cnt number of flows to add
570
 * \param family AF_INET to set IPv4 count or AF_INET6 to set IPv6 count
571
 */
572
void LiveDevAddBypassSuccess(LiveDevice *dev, uint64_t cnt, int family)
573
0
{
574
0
    BypassInfo *bpfdata = LiveDevGetStorageById(dev, g_bypass_storage_id);
575
0
    if (bpfdata) {
576
0
        if (family == AF_INET) {
577
0
            SC_ATOMIC_ADD(bpfdata->ipv4_success, cnt);
578
0
        } else if (family == AF_INET6) {
579
0
            SC_ATOMIC_ADD(bpfdata->ipv6_success, cnt);
580
0
        }
581
0
    }
582
0
}
583
584
#ifdef BUILD_UNIX_SOCKET
585
TmEcode LiveDeviceGetBypassedStats(json_t *cmd, json_t *answer, void *data)
586
0
{
587
0
    if (g_bypass_storage_id.id < 0) {
588
0
        json_object_set_new(answer, "message", json_string("Bypass not enabled"));
589
0
        SCReturnInt(TM_ECODE_FAILED);
590
0
    }
591
0
    LiveDevice *ldev = NULL, *ndev = NULL;
592
0
    json_t *ifaces = NULL;
593
0
    while(LiveDeviceForEach(&ldev, &ndev)) {
594
0
        BypassInfo *bpinfo = LiveDevGetStorageById(ldev, g_bypass_storage_id);
595
0
        if (bpinfo) {
596
0
            uint64_t ipv4_hash_count = SC_ATOMIC_GET(bpinfo->ipv4_hash_count);
597
0
            uint64_t ipv6_hash_count = SC_ATOMIC_GET(bpinfo->ipv6_hash_count);
598
0
            uint64_t ipv4_success = SC_ATOMIC_GET(bpinfo->ipv4_success);
599
0
            uint64_t ipv4_fail = SC_ATOMIC_GET(bpinfo->ipv4_fail);
600
0
            uint64_t ipv6_success = SC_ATOMIC_GET(bpinfo->ipv6_success);
601
0
            uint64_t ipv6_fail = SC_ATOMIC_GET(bpinfo->ipv6_fail);
602
0
            json_t *iface = json_object();
603
0
            if (ifaces == NULL) {
604
0
                ifaces = json_object();
605
0
                if (ifaces == NULL) {
606
0
                    json_object_set_new(answer, "message",
607
0
                            json_string("internal error at json object creation"));
608
0
                    return TM_ECODE_FAILED;
609
0
                }
610
0
            }
611
0
            json_object_set_new(iface, "ipv4_maps_count", json_integer(ipv4_hash_count));
612
0
            json_object_set_new(iface, "ipv4_success", json_integer(ipv4_success));
613
0
            json_object_set_new(iface, "ipv4_fail", json_integer(ipv4_fail));
614
0
            json_object_set_new(iface, "ipv6_maps_count", json_integer(ipv6_hash_count));
615
0
            json_object_set_new(iface, "ipv6_success", json_integer(ipv6_success));
616
0
            json_object_set_new(iface, "ipv6_fail", json_integer(ipv6_fail));
617
0
            json_object_set_new(ifaces, ldev->dev, iface);
618
0
        }
619
0
    }
620
0
    if (ifaces) {
621
0
        json_object_set_new(answer, "message", ifaces);
622
0
        SCReturnInt(TM_ECODE_OK);
623
0
    }
624
625
0
    json_object_set_new(answer, "message",
626
0
                        json_string("No interface using bypass"));
627
0
    SCReturnInt(TM_ECODE_FAILED);
628
0
}
629
#endif