Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/source-ipfw.c
Line
Count
Source
1
/* Copyright (C) 2007-2014 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 Nick Rogness <nick@rogness.net>
22
 * \author Eric Leblond <eric@regit.org>
23
 *
24
 * IPFW packet acquisition support
25
 */
26
27
#include "suricata-common.h"
28
#include "suricata.h"
29
#include "decode.h"
30
#include "packet.h"
31
#include "packet-queue.h"
32
#include "threads.h"
33
#include "threadvars.h"
34
#include "tm-queuehandlers.h"
35
#include "tm-threads.h"
36
#include "source-ipfw.h"
37
#include "util-debug.h"
38
#include "conf.h"
39
#include "util-byte.h"
40
#include "util-privs.h"
41
#include "util-datalink.h"
42
#include "util-device.h"
43
#include "runmodes.h"
44
45
#define IPFW_ACCEPT 0
46
#define IPFW_DROP 1
47
48
#define IPFW_SOCKET_POLL_MSEC 300
49
50
#ifndef IP_MAXPACKET
51
#define IP_MAXPACKET 65535
52
#endif
53
54
#ifndef IPFW
55
/* Handle the case if --enable-ipfw was not used
56
 *
57
 */
58
59
TmEcode NoIPFWSupportExit(ThreadVars *, const void *, void **);
60
61
void TmModuleReceiveIPFWRegister (void)
62
71
{
63
64
71
    tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW";
65
71
    tmm_modules[TMM_RECEIVEIPFW].ThreadInit = NoIPFWSupportExit;
66
71
    tmm_modules[TMM_RECEIVEIPFW].Func = NULL;
67
71
    tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = NULL;
68
71
    tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = NULL;
69
71
    tmm_modules[TMM_RECEIVEIPFW].flags = TM_FLAG_RECEIVE_TM;
70
71
}
71
72
void TmModuleVerdictIPFWRegister (void)
73
71
{
74
71
    tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW";
75
71
    tmm_modules[TMM_VERDICTIPFW].ThreadInit = NoIPFWSupportExit;
76
71
    tmm_modules[TMM_VERDICTIPFW].Func = NULL;
77
71
    tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = NULL;
78
71
    tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = NULL;
79
71
    tmm_modules[TMM_VERDICTIPFW].flags = TM_FLAG_VERDICT_TM;
80
71
}
81
82
void TmModuleDecodeIPFWRegister (void)
83
71
{
84
71
    tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW";
85
71
    tmm_modules[TMM_DECODEIPFW].ThreadInit = NoIPFWSupportExit;
86
71
    tmm_modules[TMM_DECODEIPFW].Func = NULL;
87
71
    tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL;
88
71
    tmm_modules[TMM_DECODEIPFW].ThreadDeinit = NULL;
89
71
    tmm_modules[TMM_DECODEIPFW].cap_flags = 0;
90
71
    tmm_modules[TMM_DECODEIPFW].flags = TM_FLAG_DECODE_TM;
91
71
}
92
93
TmEcode NoIPFWSupportExit(ThreadVars *tv, const void *initdata, void **data)
94
0
{
95
96
0
    SCLogError("Error creating thread %s: you do not have support for ipfw "
97
0
               "enabled please recompile with --enable-ipfw",
98
0
            tv->name);
99
    exit(EXIT_FAILURE);
100
0
}
101
102
#else /* We have IPFW compiled in */
103
104
#include "action-globals.h"
105
106
extern uint16_t max_pending_packets;
107
108
/**
109
 * \brief Structure to hold thread specific variables.
110
 */
111
typedef struct IPFWThreadVars_
112
{
113
    /* data link type for the thread, probably not needed */
114
    int datalink;
115
116
    /* this one should be not changing after init */
117
    uint16_t port_num;
118
    /* position into the NFQ queue var array */
119
    uint16_t ipfw_index;
120
121
    /* counters */
122
    uint32_t pkts;
123
    uint64_t bytes;
124
    uint32_t errs;
125
    uint32_t accepted;
126
    uint32_t dropped;
127
} IPFWThreadVars;
128
129
static IPFWThreadVars ipfw_t[IPFW_MAX_QUEUE];
130
static IPFWQueueVars ipfw_q[IPFW_MAX_QUEUE];
131
static uint16_t receive_port_num = 0;
132
static SCMutex ipfw_init_lock;
133
134
/* IPFW Prototypes */
135
static void *IPFWGetQueue(int number);
136
static TmEcode ReceiveIPFWThreadInit(ThreadVars *, const void *, void **);
137
static TmEcode ReceiveIPFWLoop(ThreadVars *tv, void *data, void *slot);
138
static void ReceiveIPFWThreadExitStats(ThreadVars *, void *);
139
static TmEcode ReceiveIPFWThreadDeinit(ThreadVars *, void *);
140
141
static TmEcode IPFWSetVerdict(ThreadVars *, IPFWThreadVars *, Packet *);
142
static TmEcode VerdictIPFW(ThreadVars *, Packet *, void *);
143
static TmEcode VerdictIPFWThreadInit(ThreadVars *, const void *, void **);
144
static void VerdictIPFWThreadExitStats(ThreadVars *, void *);
145
static TmEcode VerdictIPFWThreadDeinit(ThreadVars *, void *);
146
147
static TmEcode DecodeIPFWThreadInit(ThreadVars *, const void *, void **);
148
static TmEcode DecodeIPFWThreadDeinit(ThreadVars *tv, void *data);
149
static TmEcode DecodeIPFW(ThreadVars *, Packet *, void *);
150
151
/**
152
 * \brief Registration Function for RecieveIPFW.
153
 * \todo Unit tests are needed for this module.
154
 */
155
void TmModuleReceiveIPFWRegister (void)
156
{
157
    SCMutexInit(&ipfw_init_lock, NULL);
158
159
    tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW";
160
    tmm_modules[TMM_RECEIVEIPFW].ThreadInit = ReceiveIPFWThreadInit;
161
    tmm_modules[TMM_RECEIVEIPFW].Func = NULL;
162
    tmm_modules[TMM_RECEIVEIPFW].PktAcqLoop = ReceiveIPFWLoop;
163
    tmm_modules[TMM_RECEIVEIPFW].PktAcqBreakLoop = NULL;
164
    tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = ReceiveIPFWThreadExitStats;
165
    tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = ReceiveIPFWThreadDeinit;
166
    tmm_modules[TMM_RECEIVEIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
167
                                             SC_CAP_NET_BIND_SERVICE |
168
                                             SC_CAP_NET_BROADCAST; /** \todo untested */
169
    tmm_modules[TMM_RECEIVEIPFW].flags = TM_FLAG_RECEIVE_TM;
170
}
171
172
/**
173
 * \brief Registration Function for VerdictIPFW.
174
 * \todo Unit tests are needed for this module.
175
 */
176
void TmModuleVerdictIPFWRegister (void)
177
{
178
    tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW";
179
    tmm_modules[TMM_VERDICTIPFW].ThreadInit = VerdictIPFWThreadInit;
180
    tmm_modules[TMM_VERDICTIPFW].Func = VerdictIPFW;
181
    tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = VerdictIPFWThreadExitStats;
182
    tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = VerdictIPFWThreadDeinit;
183
    tmm_modules[TMM_VERDICTIPFW].cap_flags = SC_CAP_NET_ADMIN | SC_CAP_NET_RAW |
184
                                             SC_CAP_NET_BIND_SERVICE; /** \todo untested */
185
    tmm_modules[TMM_VERDICTIPFW].flags = TM_FLAG_VERDICT_TM;
186
}
187
188
/**
189
 * \brief Registration Function for DecodeIPFW.
190
 * \todo Unit tests are needed for this module.
191
 */
192
void TmModuleDecodeIPFWRegister (void)
193
{
194
    tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW";
195
    tmm_modules[TMM_DECODEIPFW].ThreadInit = DecodeIPFWThreadInit;
196
    tmm_modules[TMM_DECODEIPFW].Func = DecodeIPFW;
197
    tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL;
198
    tmm_modules[TMM_DECODEIPFW].ThreadDeinit = DecodeIPFWThreadDeinit;
199
    tmm_modules[TMM_DECODEIPFW].flags = TM_FLAG_DECODE_TM;
200
}
201
202
static inline void IPFWMutexInit(IPFWQueueVars *nq)
203
{
204
    char *active_runmode = RunmodeGetActive();
205
206
    if (active_runmode && !strcmp("workers", active_runmode)) {
207
        nq->use_mutex = 0;
208
        SCLogInfo("IPFW running in 'workers' runmode, will not use mutex.");
209
    } else {
210
        nq->use_mutex = 1;
211
    }
212
    if (nq->use_mutex)
213
        SCMutexInit(&nq->socket_lock, NULL);
214
}
215
216
static inline void IPFWMutexLock(IPFWQueueVars *nq)
217
{
218
    if (nq->use_mutex)
219
        SCMutexLock(&nq->socket_lock);
220
}
221
222
static inline void IPFWMutexUnlock(IPFWQueueVars *nq)
223
{
224
    if (nq->use_mutex)
225
        SCMutexUnlock(&nq->socket_lock);
226
}
227
228
TmEcode ReceiveIPFWLoop(ThreadVars *tv, void *data, void *slot)
229
{
230
    SCEnter();
231
232
    IPFWThreadVars *ptv = (IPFWThreadVars *)data;
233
    IPFWQueueVars *nq = NULL;
234
    uint8_t pkt[IP_MAXPACKET];
235
    int pktlen=0;
236
    struct pollfd IPFWpoll;
237
    struct timeval IPFWts;
238
    Packet *p = NULL;
239
240
    nq = IPFWGetQueue(ptv->ipfw_index);
241
    if (nq == NULL) {
242
        SCLogWarning("Can't get thread variable");
243
        SCReturnInt(TM_ECODE_FAILED);
244
    }
245
246
    SCLogInfo("Thread '%s' will run on port %d (item %d)",
247
              tv->name, nq->port_num, ptv->ipfw_index);
248
249
    // Indicate that the thread is actually running its application level code (i.e., it can poll
250
    // packets)
251
    TmThreadsSetFlag(tv, THV_RUNNING);
252
253
    while (1) {
254
        if (unlikely(suricata_ctl_flags != 0)) {
255
            SCReturnInt(TM_ECODE_OK);
256
        }
257
258
        IPFWpoll.fd = nq->fd;
259
        IPFWpoll.events = POLLRDNORM;
260
        /* Poll the socket for status */
261
        if ( (poll(&IPFWpoll, 1, IPFW_SOCKET_POLL_MSEC)) > 0) {
262
            if (!(IPFWpoll.revents & (POLLRDNORM | POLLERR)))
263
                continue;
264
        }
265
266
        if ((pktlen = recvfrom(nq->fd, pkt, sizeof(pkt), 0,
267
                               (struct sockaddr *)&nq->ipfw_sin,
268
                               &nq->ipfw_sinlen)) == -1) {
269
            /* We received an error on socket read */
270
            if (errno == EINTR || errno == EWOULDBLOCK) {
271
                /* Nothing for us to process */
272
                continue;
273
            } else {
274
                SCLogWarning("Read from IPFW divert socket failed: %s", strerror(errno));
275
                SCReturnInt(TM_ECODE_FAILED);
276
            }
277
        }
278
        /* We have a packet to process */
279
        memset (&IPFWts, 0, sizeof(struct timeval));
280
        gettimeofday(&IPFWts, NULL);
281
282
        /* make sure we have at least one packet in the packet pool, to prevent
283
         * us from alloc'ing packets at line rate */
284
        PacketPoolWait();
285
286
        p = PacketGetFromQueueOrAlloc();
287
        if (p == NULL) {
288
            SCReturnInt(TM_ECODE_FAILED);
289
        }
290
        PKT_SET_SRC(p, PKT_SRC_WIRE);
291
292
        SCLogDebug("Received Packet Len: %d", pktlen);
293
294
        p->ts = SCTIME_FROM_TIMEVAL(&IPFWts);
295
296
        ptv->pkts++;
297
        ptv->bytes += pktlen;
298
299
        p->datalink = ptv->datalink;
300
301
        p->ipfw_v.ipfw_index = ptv->ipfw_index;
302
303
        PacketCopyData(p, pkt, pktlen);
304
        SCLogDebug("Packet info: pkt_len: %" PRIu32 " (pkt %02x, pkt_data %02x)",
305
                   GET_PKT_LEN(p), *pkt, *(GET_PKT_DATA(p)));
306
307
        if (TmThreadsSlotProcessPkt(tv, ((TmSlot *) slot)->slot_next, p)
308
                != TM_ECODE_OK) {
309
            SCReturnInt(TM_ECODE_FAILED);
310
        }
311
312
        StatsSyncCountersIfSignalled(tv);
313
    }
314
315
    SCReturnInt(TM_ECODE_OK);
316
}
317
318
/**
319
 * \brief Init function for RecieveIPFW.
320
 *
321
 * This is a setup function for receiving packets
322
 * via ipfw divert, binds a socket, and prepares to
323
 * to read from it.
324
 *
325
 * \param tv pointer to ThreadVars
326
 * \param initdata pointer to the divert port passed from the user
327
 * \param data pointer gets populated with IPFWThreadVars
328
 *
329
 */
330
TmEcode ReceiveIPFWThreadInit(ThreadVars *tv, const void *initdata, void **data)
331
{
332
    struct timeval timev;
333
    IPFWThreadVars *ntv = (IPFWThreadVars *) initdata;
334
    IPFWQueueVars *nq = IPFWGetQueue(ntv->ipfw_index);
335
336
    sigset_t sigs;
337
    sigfillset(&sigs);
338
    pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
339
340
    SCEnter();
341
342
    IPFWMutexInit(nq);
343
    /* We need a divert socket to play with */
344
#ifdef PF_DIVERT
345
    if ((nq->fd = socket(PF_DIVERT, SOCK_RAW, 0)) == -1) {
346
#else
347
    if ((nq->fd = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) {
348
#endif
349
        SCLogError("Can't create divert socket: %s", strerror(errno));
350
        SCReturnInt(TM_ECODE_FAILED);
351
    }
352
353
    /* set a timeout to the socket so we can check for a signal
354
     * in case we don't get packets for a longer period. */
355
    timev.tv_sec = 1;
356
    timev.tv_usec = 0;
357
358
    if (setsockopt(nq->fd, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)) == -1) {
359
        SCLogError("Can't set IPFW divert socket timeout: %s", strerror(errno));
360
        SCReturnInt(TM_ECODE_FAILED);
361
    }
362
363
    nq->ipfw_sinlen=sizeof(nq->ipfw_sin);
364
    memset(&nq->ipfw_sin, 0, nq->ipfw_sinlen);
365
    nq->ipfw_sin.sin_family = PF_INET;
366
    nq->ipfw_sin.sin_addr.s_addr = INADDR_ANY;
367
    nq->ipfw_sin.sin_port = htons(nq->port_num);
368
369
    /* Bind that SOB */
370
    if (bind(nq->fd, (struct sockaddr *)&nq->ipfw_sin, nq->ipfw_sinlen) == -1) {
371
        SCLogError("Can't bind divert socket on port %d: %s", nq->port_num, strerror(errno));
372
        SCReturnInt(TM_ECODE_FAILED);
373
    }
374
375
    ntv->datalink = DLT_RAW;
376
    DatalinkSetGlobalType(DLT_RAW);
377
378
    *data = (void *)ntv;
379
380
    SCReturnInt(TM_ECODE_OK);
381
}
382
383
/**
384
 * \brief This function prints stats to the screen at exit.
385
 * \todo Unit tests are needed for this module.
386
 * \param tv pointer to ThreadVars
387
 * \param data pointer that gets cast into IPFWThreadVars for ptv
388
 */
389
void ReceiveIPFWThreadExitStats(ThreadVars *tv, void *data)
390
{
391
    IPFWThreadVars *ptv = (IPFWThreadVars *)data;
392
393
    SCEnter();
394
395
    SCLogNotice("(%s) Treated: Pkts %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "",
396
            tv->name, ptv->pkts, ptv->bytes, ptv->errs);
397
    SCLogNotice("(%s) Verdict: Accepted %"PRIu32", Dropped %"PRIu32 "",
398
            tv->name, ptv->accepted, ptv->dropped);
399
400
401
    SCReturn;
402
}
403
404
/**
405
 * \brief DeInit function closes divert socket at exit.
406
 * \todo Unit tests are needed for this module.
407
 * \param tv pointer to ThreadVars
408
 * \param data pointer that gets cast into IPFWThreadVars for ptv
409
 */
410
TmEcode ReceiveIPFWThreadDeinit(ThreadVars *tv, void *data)
411
{
412
    IPFWThreadVars *ptv = (IPFWThreadVars *)data;
413
    IPFWQueueVars *nq = IPFWGetQueue(ptv->ipfw_index);
414
415
    SCEnter();
416
417
    if (close(nq->fd) < 0) {
418
        SCLogWarning("Unable to disable ipfw socket: %s", strerror(errno));
419
        SCReturnInt(TM_ECODE_FAILED);
420
    }
421
422
    SCReturnInt(TM_ECODE_OK);
423
}
424
425
/**
426
 * \brief This function passes off to link type decoders.
427
 * \todo Unit tests are needed for this module.
428
 *
429
 * DecodeIPFW decodes packets from IPFW and passes
430
 * them off to the proper link type decoder.
431
 *
432
 * \param tv pointer to ThreadVars
433
 * \param p pointer to the current packet
434
 * \param data pointer that gets cast into IPFWThreadVars for ptv
435
 */
436
TmEcode DecodeIPFW(ThreadVars *tv, Packet *p, void *data)
437
{
438
    IPV4Hdr *ip4h = (IPV4Hdr *)GET_PKT_DATA(p);
439
    IPV6Hdr *ip6h = (IPV6Hdr *)GET_PKT_DATA(p);
440
    DecodeThreadVars *dtv = (DecodeThreadVars *)data;
441
442
    SCEnter();
443
444
    BUG_ON(PKT_IS_PSEUDOPKT(p));
445
446
    /* update counters */
447
    DecodeUpdatePacketCounters(tv, dtv, p);
448
449
    /* Process IP packets */
450
    if (IPV4_GET_RAW_VER(ip4h) == 4) {
451
        if (unlikely(GET_PKT_LEN(p) > USHRT_MAX)) {
452
            return TM_ECODE_FAILED;
453
        }
454
        SCLogDebug("DecodeIPFW ip4 processing");
455
        DecodeIPV4(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
456
457
    } else if(IPV6_GET_RAW_VER(ip6h) == 6) {
458
        if (unlikely(GET_PKT_LEN(p) > USHRT_MAX)) {
459
            return TM_ECODE_FAILED;
460
        }
461
        SCLogDebug("DecodeIPFW ip6 processing");
462
        DecodeIPV6(tv, dtv, p, GET_PKT_DATA(p), GET_PKT_LEN(p));
463
464
    } else {
465
        /* We don't support anything besides IP packets for now, bridged packets? */
466
        SCLogInfo("IPFW unknown protocol support %02x", *GET_PKT_DATA(p));
467
       SCReturnInt(TM_ECODE_FAILED);
468
    }
469
470
    PacketDecodeFinalize(tv, dtv, p);
471
472
    SCReturnInt(TM_ECODE_OK);
473
}
474
475
/**
476
 * \brief This function initializes the DecodeThreadVariables
477
 *
478
 *
479
 * \param tv pointer to ThreadVars
480
 * \param initdata pointer for passing in args
481
 * \param data pointer that gets cast into IPFWThreadVars for ptv
482
 */
483
TmEcode DecodeIPFWThreadInit(ThreadVars *tv, const void *initdata, void **data)
484
{
485
    DecodeThreadVars *dtv = NULL;
486
    dtv = DecodeThreadVarsAlloc(tv);
487
488
    if (dtv == NULL)
489
        SCReturnInt(TM_ECODE_FAILED);
490
491
    DecodeRegisterPerfCounters(dtv, tv);
492
493
    *data = (void *)dtv;
494
495
    SCReturnInt(TM_ECODE_OK);
496
}
497
498
TmEcode DecodeIPFWThreadDeinit(ThreadVars *tv, void *data)
499
{
500
    if (data != NULL)
501
        DecodeThreadVarsFree(tv, data);
502
    SCReturnInt(TM_ECODE_OK);
503
}
504
505
/**
506
 * \brief This function sets the Verdict and processes the packet
507
 *
508
 *
509
 * \param tv pointer to ThreadVars
510
 * \param p pointer to the Packet
511
 */
512
TmEcode IPFWSetVerdict(ThreadVars *tv, IPFWThreadVars *ptv, Packet *p)
513
{
514
    uint32_t verdict;
515
#if 0
516
    struct pollfd IPFWpoll;
517
#endif
518
    IPFWQueueVars *nq = NULL;
519
520
    SCEnter();
521
522
    if (p == NULL) {
523
        SCLogWarning("Packet is NULL");
524
        SCReturnInt(TM_ECODE_FAILED);
525
    }
526
527
    nq = IPFWGetQueue(p->ipfw_v.ipfw_index);
528
    if (nq == NULL) {
529
        SCLogWarning("No thread found");
530
        SCReturnInt(TM_ECODE_FAILED);
531
    }
532
533
#if 0
534
    IPFWpoll.fd = nq->fd;
535
    IPFWpoll.events = POLLWRNORM;
536
#endif
537
538
    if (PacketCheckAction(p, ACTION_DROP)) {
539
        verdict = IPFW_DROP;
540
    } else {
541
        verdict = IPFW_ACCEPT;
542
    }
543
544
    if (verdict == IPFW_ACCEPT) {
545
        SCLogDebug("IPFW Verdict is to Accept");
546
        ptv->accepted++;
547
548
        /* For divert sockets, accepting means writing the
549
         * packet back to the socket for ipfw to pick up
550
         */
551
        SCLogDebug("IPFWSetVerdict writing to socket %d, %p, %u", nq->fd, GET_PKT_DATA(p),GET_PKT_LEN(p));
552
553
#if 0
554
        while ((poll(&IPFWpoll,1,IPFW_SOCKET_POLL_MSEC)) < 1) {
555
            /* Did we receive a signal to shutdown */
556
            if (TmThreadsCheckFlag(tv, THV_KILL) || TmThreadsCheckFlag(tv, THV_PAUSE)) {
557
                SCLogInfo("Received ThreadShutdown: IPFW divert socket writing interrupted");
558
                SCReturnInt(TM_ECODE_OK);
559
            }
560
        }
561
#endif
562
563
        IPFWMutexLock(nq);
564
        if (sendto(nq->fd, GET_PKT_DATA(p), GET_PKT_LEN(p), 0,(struct sockaddr *)&nq->ipfw_sin, nq->ipfw_sinlen) == -1) {
565
            int r = errno;
566
            switch (r) {
567
                default:
568
                    SCLogWarning("Write to ipfw divert socket failed: %s", strerror(r));
569
                    IPFWMutexUnlock(nq);
570
                    SCReturnInt(TM_ECODE_FAILED);
571
                case EHOSTDOWN:
572
                case ENETDOWN:
573
                    break;
574
            }
575
        }
576
577
        IPFWMutexUnlock(nq);
578
579
        SCLogDebug("Sent Packet back into IPFW Len: %d",GET_PKT_LEN(p));
580
581
    } /* end IPFW_ACCEPT */
582
583
584
    if (verdict == IPFW_DROP) {
585
        SCLogDebug("IPFW SetVerdict is to DROP");
586
        ptv->dropped++;
587
588
        /** \todo For divert sockets, dropping means not writing the packet back to the socket.
589
         * Need to see if there is some better way to free the packet from the queue */
590
591
    } /* end IPFW_DROP */
592
593
    SCReturnInt(TM_ECODE_OK);
594
}
595
596
597
/**
598
 * \brief This function handles the Verdict processing
599
 * \todo Unit tests are needed for this module.
600
 *
601
 *
602
 * \param tv pointer to ThreadVars
603
 * \param p pointer to the Packet
604
 * \param data pointer that gets cast into IPFWThreadVars for ptv
605
 */
606
TmEcode VerdictIPFW(ThreadVars *tv, Packet *p, void *data)
607
{
608
    IPFWThreadVars *ptv = (IPFWThreadVars *)data;
609
    TmEcode retval = TM_ECODE_OK;
610
611
    SCEnter();
612
613
    /* can't verdict a "fake" packet */
614
    if (p->flags & PKT_PSEUDO_STREAM_END) {
615
        SCReturnInt(TM_ECODE_OK);
616
    }
617
618
    /* This came from NFQ.
619
     *  if this is a tunnel packet we check if we are ready to verdict
620
     * already. */
621
    if (IS_TUNNEL_PKT(p)) {
622
        bool verdict = VerdictTunnelPacket(p);
623
624
        /* don't verdict if we are not ready */
625
        if (verdict == true) {
626
            SCLogDebug("Setting verdict on tunnel");
627
            retval = IPFWSetVerdict(tv, ptv, p->root ? p->root : p);
628
        }
629
    } else {
630
        /* no tunnel, verdict normally */
631
        SCLogDebug("Setting verdict on non-tunnel");
632
        retval = IPFWSetVerdict(tv, ptv, p);
633
    } /* IS_TUNNEL_PKT end */
634
635
    SCReturnInt(retval);
636
}
637
638
/**
639
 * \brief This function initializes the VerdictThread
640
 *
641
 *
642
 * \param t pointer to ThreadVars
643
 * \param initdata pointer for passing in args
644
 * \param data pointer that gets cast into IPFWThreadVars for ptv
645
 */
646
TmEcode VerdictIPFWThreadInit(ThreadVars *tv, const void *initdata, void **data)
647
{
648
649
    IPFWThreadVars *ptv = NULL;
650
651
    SCEnter();
652
653
    /* Setup Thread vars */
654
    if ( (ptv = SCMalloc(sizeof(IPFWThreadVars))) == NULL)
655
        SCReturnInt(TM_ECODE_FAILED);
656
    memset(ptv, 0, sizeof(IPFWThreadVars));
657
658
659
    *data = (void *)ptv;
660
661
    SCReturnInt(TM_ECODE_OK);
662
}
663
664
/**
665
 * \brief This function deinitializes the VerdictThread
666
 *
667
 *
668
 * \param tv pointer to ThreadVars
669
 * \param data pointer that gets cast into IPFWThreadVars for ptv
670
 */
671
TmEcode VerdictIPFWThreadDeinit(ThreadVars *tv, void *data)
672
{
673
674
    SCEnter();
675
676
    /* We don't need to do anything...not sure quite yet */
677
678
679
    SCReturnInt(TM_ECODE_OK);
680
}
681
682
/**
683
 * \brief This function prints stats for the VerdictThread
684
 *
685
 *
686
 * \param tv pointer to ThreadVars
687
 * \param data pointer that gets cast into IPFWThreadVars for ptv
688
 */
689
void VerdictIPFWThreadExitStats(ThreadVars *tv, void *data)
690
{
691
    IPFWThreadVars *ptv = (IPFWThreadVars *)data;
692
    SCLogInfo("IPFW Processing: - (%s) Pkts accepted %" PRIu32 ", dropped %" PRIu32 "", tv->name, ptv->accepted, ptv->dropped);
693
}
694
695
/**
696
 *  \brief Add an IPFW divert
697
 *
698
 *  \param string with the queue name
699
 *
700
 *  \retval 0 on success.
701
 *  \retval -1 on failure.
702
 */
703
int IPFWRegisterQueue(char *queue)
704
{
705
    IPFWThreadVars *ntv = NULL;
706
    IPFWQueueVars *nq = NULL;
707
    /* Extract the queue number from the specified command line argument */
708
    uint16_t port_num = 0;
709
    if ((StringParseUint16(&port_num, 10, strlen(queue), queue)) < 0)
710
    {
711
        SCLogError("specified queue number %s is not "
712
                   "valid",
713
                queue);
714
        return -1;
715
    }
716
717
    SCMutexLock(&ipfw_init_lock);
718
    if (receive_port_num >= IPFW_MAX_QUEUE) {
719
        SCLogError("too much IPFW divert port registered (%d)", receive_port_num);
720
        SCMutexUnlock(&ipfw_init_lock);
721
        return -1;
722
    }
723
    if (receive_port_num == 0) {
724
        memset(&ipfw_t, 0, sizeof(ipfw_t));
725
        memset(&ipfw_q, 0, sizeof(ipfw_q));
726
    }
727
728
    ntv = &ipfw_t[receive_port_num];
729
    ntv->ipfw_index = receive_port_num;
730
731
    nq = &ipfw_q[receive_port_num];
732
    nq->port_num = port_num;
733
    receive_port_num++;
734
    SCMutexUnlock(&ipfw_init_lock);
735
    LiveRegisterDeviceName(queue);
736
737
    SCLogDebug("Queue \"%s\" registered.", queue);
738
    return 0;
739
}
740
741
/**
742
 *  \brief Get a pointer to the IPFW queue at index
743
 *
744
 *  \param number idx of the queue in our array
745
 *
746
 *  \retval ptr pointer to the IPFWThreadVars at index
747
 *  \retval NULL on error
748
 */
749
void *IPFWGetQueue(int number)
750
{
751
    if (number >= receive_port_num)
752
        return NULL;
753
754
    return (void *)&ipfw_q[number];
755
}
756
757
/**
758
 *  \brief Get a pointer to the IPFW thread at index
759
 *
760
 *  This function is temporary used as configuration parser.
761
 *
762
 *  \param number idx of the queue in our array
763
 *
764
 *  \retval ptr pointer to the IPFWThreadVars at index
765
 *  \retval NULL on error
766
 */
767
void *IPFWGetThread(int number)
768
{
769
    if (number >= receive_port_num)
770
        return NULL;
771
772
    return (void *)&ipfw_t[number];
773
}
774
775
#endif /* End ifdef IPFW */
776
777
/* eof */
778