From 2b7b78f1bfca56eebc206c206e20f0f419fc05cd Mon Sep 17 00:00:00 2001 From: Nick Rogness Date: Wed, 3 Feb 2010 17:21:10 -0600 Subject: [PATCH] Intial IPFW support FreeBSD and OSX --- configure.in | 9 + doc/INSTALL | 16 +- src/Makefile.am | 1 + src/respond-reject-libnet11.c | 2 +- src/runmodes.c | 136 ++++++++ src/runmodes.h | 1 + src/source-ipfw.c | 631 ++++++++++++++++++++++++++++++++++ src/source-ipfw.h | 22 ++ src/suricata.c | 18 +- src/suricata.h | 1 + src/threads.h | 21 ++ src/tm-modules.h | 3 + src/tm-threads.c | 11 +- src/util-debug-filters.h | 13 - src/util-debug.c | 4 +- src/util-error.c | 8 + src/util-error.h | 8 + suricata.yaml | 30 +- 18 files changed, 909 insertions(+), 26 deletions(-) create mode 100644 src/source-ipfw.c create mode 100644 src/source-ipfw.h diff --git a/configure.in b/configure.in index fac71a7af8..47fee5d617 100644 --- a/configure.in +++ b/configure.in @@ -364,6 +364,15 @@ AC_INIT(configure.in) fi fi +#enable support for IPFW + AC_ARG_ENABLE(ipfw, + [ --enable-ipfw Enable FreeBSD IPFW support for inline IDP], + [ enable_ipfw=yes + ]) + if test "$enable_ipfw" = "yes"; then + CFLAGS="$CFLAGS -DIPFW" + fi + #libnet AC_ARG_WITH(libnet_includes, [ --with-libnet-includes=DIR libnet include directory], diff --git a/doc/INSTALL b/doc/INSTALL index ced9514d8f..1f97d8119a 100644 --- a/doc/INSTALL +++ b/doc/INSTALL @@ -256,8 +256,20 @@ For FreeBSD 8 Users sysctl net.bpf.zerocopy_enable=1 - - + #if you would like to build suricata on FreeBSD with IPS capabilities with IPFW via --enable-ipfw. + You must do the following to enable ipfw and divert socket support before starting the engine + with -d. + + #edit /etc/rc.conf and add or modify the following lines + firewall_enable="YES" + firewall_type="open" + + #edit /boot/loader.conf and add or modify the following lines + ipfw_load="YES" + ipfw_nat_load="YES" + ipdivert_load="YES" + dummynet_load="YES" + libalias_load="YES" Basic Installation diff --git a/src/Makefile.am b/src/Makefile.am index af85d3608a..dc06c7f3c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,6 +11,7 @@ source-nfq.c source-nfq.h \ source-pcap.c source-pcap.h \ source-pcap-file.c source-pcap-file.h \ source-pfring.c source-pfring.h \ +source-ipfw.c source-ipfw.h \ decode.c decode.h \ decode-ethernet.c decode-ethernet.h \ decode-sll.c decode-sll.h \ diff --git a/src/respond-reject-libnet11.c b/src/respond-reject-libnet11.c index a421ab9d81..0842927fa5 100644 --- a/src/respond-reject-libnet11.c +++ b/src/respond-reject-libnet11.c @@ -109,7 +109,7 @@ int RejectSendLibnet11L3IPv4TCP(ThreadVars *tv, Packet *p, void *data, int dir) lpacket.sp, /* source port */ lpacket.dp, /* dst port */ lpacket.seq, /* seq number */ - lpacket.ack, /* ack number */ + lpacket.ack+1, /* ack number */ TH_RST|TH_ACK, /* flags */ lpacket.window, /* window size */ 0, /* checksum */ diff --git a/src/runmodes.c b/src/runmodes.c index ecc4cb1d34..8a3d06d1a2 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -1411,6 +1411,142 @@ int RunModeIdsPfring3(DetectEngineCtx *de_ctx, char *iface) { return 0; } +int RunModeIpsIPFW(DetectEngineCtx *de_ctx) { + + TimeModeSetLive(); + + /* create the threads */ + ThreadVars *tv_receiveipfw = TmThreadCreatePacketHandler("ReceiveIPFW","packetpool","packetpool","pickup-queue","simple","1slot_noinout"); + + if (tv_receiveipfw == NULL) { + printf("ERROR: TmThreadsCreate failed\n"); + exit(EXIT_FAILURE); + } + TmModule *tm_module = TmModuleGetByName("ReceiveIPFW"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName failed for ReceiveIPFW\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_receiveipfw,tm_module,NULL); + + if (TmThreadSpawn(tv_receiveipfw) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_decode1 = TmThreadCreatePacketHandler("Decode1","pickup-queue","simple","decode-queue1","simple","1slot"); + if (tv_decode1 == NULL) { + printf("ERROR: TmThreadsCreate failed for Decode1\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("DecodeIPFW"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName DecodeIPFW failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_decode1,tm_module,NULL); + + if (TmThreadSpawn(tv_decode1) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_stream1 = TmThreadCreatePacketHandler("Stream1","decode-queue1","simple","stream-queue1","simple","1slot"); + if (tv_stream1 == NULL) { + printf("ERROR: TmThreadsCreate failed for Stream1\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("StreamTcp"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName StreamTcp failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_stream1,tm_module,NULL); + + if (TmThreadSpawn(tv_stream1) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_detect1 = TmThreadCreatePacketHandler("Detect1","stream-queue1","simple","verdict-queue","simple","1slot"); + if (tv_detect1 == NULL) { + printf("ERROR: TmThreadsCreate failed\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("Detect"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName Detect failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_detect1,tm_module,(void *)de_ctx); + + if (TmThreadSpawn(tv_detect1) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_detect2 = TmThreadCreatePacketHandler("Detect2","stream-queue1","simple","verdict-queue","simple","1slot"); + if (tv_detect2 == NULL) { + printf("ERROR: TmThreadsCreate failed\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("Detect"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName Detect failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_detect2,tm_module,(void *)de_ctx); + + if (TmThreadSpawn(tv_detect2) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_verdict = TmThreadCreatePacketHandler("Verdict","verdict-queue","simple","respond-queue","simple","1slot"); + if (tv_verdict == NULL) { + printf("ERROR: TmThreadsCreate failed\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("VerdictIPFW"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName VerdictIPFW failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_verdict,tm_module,NULL); + + if (TmThreadSpawn(tv_verdict) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_rreject = TmThreadCreatePacketHandler("RespondReject","respond-queue","simple","alert-queue1","simple","1slot"); + if (tv_rreject == NULL) { + printf("ERROR: TmThreadsCreate failed\n"); + exit(EXIT_FAILURE); + } + tm_module = TmModuleGetByName("RespondReject"); + if (tm_module == NULL) { + printf("ERROR: TmModuleGetByName for RespondReject failed\n"); + exit(EXIT_FAILURE); + } + Tm1SlotSetFunc(tv_rreject,tm_module,NULL); + + if (TmThreadSpawn(tv_rreject) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + ThreadVars *tv_outputs = TmThreadCreatePacketHandler("Outputs", + "alert-queue1", "simple", "packetpool", "packetpool", "varslot"); + SetupOutputs(tv_outputs); + if (TmThreadSpawn(tv_outputs) != TM_ECODE_OK) { + printf("ERROR: TmThreadSpawn failed\n"); + exit(EXIT_FAILURE); + } + + return 0; +} + /** RunmodeIdsPfring4 simple 4 pfring, decode, stream, and detect threads */ int RunModeIdsPfring4(DetectEngineCtx *de_ctx, char *iface) { TimeModeSetLive(); diff --git a/src/runmodes.h b/src/runmodes.h index 261f8a34ae..d289dc33b6 100644 --- a/src/runmodes.h +++ b/src/runmodes.h @@ -17,6 +17,7 @@ int RunModeIdsPfring2(DetectEngineCtx *, char *); int RunModeIdsPfring3(DetectEngineCtx *, char *); int RunModeIdsPfring4(DetectEngineCtx *, char *); +int RunModeIpsIPFW(DetectEngineCtx *); void RunModeShutDown(void); #endif /* __RUNMODES_H__ */ diff --git a/src/source-ipfw.c b/src/source-ipfw.c new file mode 100644 index 0000000000..787a6a6d51 --- /dev/null +++ b/src/source-ipfw.c @@ -0,0 +1,631 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +/** \file + * \author Nick Rogness + */ + +#include +#include +#include "suricata-common.h" +#include "suricata.h" +#include "decode.h" +#include "packet-queue.h" +#include "threads.h" +#include "threadvars.h" +#include "tm-queuehandlers.h" +#include "tm-modules.h" +#include "tm-threads.h" +#include "source-ipfw.h" +#include "util-debug.h" +#include "conf.h" + +#define IPFW_ACCEPT 0 +#define IPFW_DROP 1 + +#define IPFW_SOCKET_POLL_MSEC 300 + +#ifndef IP_MAXPACKET +#define IP_MAXPACKET 65535 +#endif + +#ifndef IPFW +/* Handle the case if --enable-ipfw was not used + * + */ + +TmEcode NoIPFWSupportExit(ThreadVars *, void *, void **); + +void TmModuleReceiveIPFWRegister (void) { + tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW"; + tmm_modules[TMM_RECEIVEIPFW].ThreadInit = NoIPFWSupportExit; + tmm_modules[TMM_RECEIVEIPFW].Func = NULL; + tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = NULL; + tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = NULL; + tmm_modules[TMM_RECEIVEIPFW].RegisterTests = NULL; +} + +void TmModuleVerdictIPFWRegister (void) { + tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW"; + tmm_modules[TMM_VERDICTIPFW].ThreadInit = NoIPFWSupportExit; + tmm_modules[TMM_VERDICTIPFW].Func = NULL; + tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = NULL; + tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = NULL; + tmm_modules[TMM_VERDICTIPFW].RegisterTests = NULL; +} + +void TmModuleDecodeIPFWRegister (void) { + tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW"; + tmm_modules[TMM_DECODEIPFW].ThreadInit = NoIPFWSupportExit; + tmm_modules[TMM_DECODEIPFW].Func = NULL; + tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL; + tmm_modules[TMM_DECODEIPFW].ThreadDeinit = NULL; + tmm_modules[TMM_DECODEIPFW].RegisterTests = NULL; +} + +TmEcode NoIPFWSupportExit(ThreadVars *tv, void *initdata, void **data) { + + SCLogError(SC_ERR_IPFW_NOSUPPORT,"Error creating thread %s: you do not have support for ipfw " + "enabled please recompile with --enable-ipfw", tv->name); + exit(EXIT_FAILURE); +} + +#else /* We have IPFW compiled in */ + +/** + * \brief Structure to hold thread specific variables. + */ +typedef struct IPFWThreadVars_ +{ + + /* data link type for the thread, probably not needed */ + int datalink; + + /* counters */ + uint32_t pkts; + uint64_t bytes; + uint32_t errs; + uint32_t accepted; + uint32_t dropped; + +} IPFWThreadVars; + +/* Global socket handler for the divert socket */ +struct sockaddr_in ipfw_sin; +socklen_t ipfw_sinlen; +int ipfw_sock; +static SCMutex ipfw_socket_lock; + +/* IPFW Prototypes */ +TmEcode ReceiveIPFWThreadInit(ThreadVars *, void *, void **); +TmEcode ReceiveIPFW(ThreadVars *, Packet *, void *, PacketQueue *); +void ReceiveIPFWThreadExitStats(ThreadVars *, void *); +TmEcode ReceiveIPFWThreadDeinit(ThreadVars *, void *); + +TmEcode IPFWSetVerdict(ThreadVars *, IPFWThreadVars *, Packet *); +TmEcode VerdictIPFW(ThreadVars *, Packet *, void *, PacketQueue *); +TmEcode VerdictIPFWThreadInit(ThreadVars *, void *, void **); +void VerdictIPFWThreadExitStats(ThreadVars *, void *); +TmEcode VerdictIPFWThreadDeinit(ThreadVars *, void *); + +TmEcode DecodeIPFWThreadInit(ThreadVars *, void *, void **); +TmEcode DecodeIPFW(ThreadVars *, Packet *, void *, PacketQueue *); + +/** + * \brief Registration Function for RecieveIPFW. + * \todo Unit tests are needed for this module. + */ +void TmModuleReceiveIPFWRegister (void) { + tmm_modules[TMM_RECEIVEIPFW].name = "ReceiveIPFW"; + tmm_modules[TMM_RECEIVEIPFW].ThreadInit = ReceiveIPFWThreadInit; + tmm_modules[TMM_RECEIVEIPFW].Func = ReceiveIPFW; + tmm_modules[TMM_RECEIVEIPFW].ThreadExitPrintStats = ReceiveIPFWThreadExitStats; + tmm_modules[TMM_RECEIVEIPFW].ThreadDeinit = ReceiveIPFWThreadDeinit; + tmm_modules[TMM_RECEIVEIPFW].RegisterTests = NULL; +} + +/** + * \brief Registration Function for VerdictIPFW. + * \todo Unit tests are needed for this module. + */ +void TmModuleVerdictIPFWRegister (void) { + tmm_modules[TMM_VERDICTIPFW].name = "VerdictIPFW"; + tmm_modules[TMM_VERDICTIPFW].ThreadInit = VerdictIPFWThreadInit; + tmm_modules[TMM_VERDICTIPFW].Func = VerdictIPFW; + tmm_modules[TMM_VERDICTIPFW].ThreadExitPrintStats = VerdictIPFWThreadExitStats; + tmm_modules[TMM_VERDICTIPFW].ThreadDeinit = VerdictIPFWThreadDeinit; + tmm_modules[TMM_VERDICTIPFW].RegisterTests = NULL; +} + +/** + * \brief Registration Function for DecodeIPFW. + * \todo Unit tests are needed for this module. + */ +void TmModuleDecodeIPFWRegister (void) { + tmm_modules[TMM_DECODEIPFW].name = "DecodeIPFW"; + tmm_modules[TMM_DECODEIPFW].ThreadInit = DecodeIPFWThreadInit; + tmm_modules[TMM_DECODEIPFW].Func = DecodeIPFW; + tmm_modules[TMM_DECODEIPFW].ThreadExitPrintStats = NULL; + tmm_modules[TMM_DECODEIPFW].ThreadDeinit = NULL; + tmm_modules[TMM_DECODEIPFW].RegisterTests = NULL; +} + +/** + * \brief Recieves packets from an interface via ipfw divert socket. + * \todo Unit tests are needed for this module. + * + * This function recieves packets from an ipfw divert socket and passes + * the packet on to the queue + * + * \param tv pointer to ThreadVars + * \param p pointer to Packet + * \param data pointer that gets cast into IPFWThreadVars for ptv + * \param pq pointer to the PacketQueue (not used here but part of the api) + * \retval TM_ECODE_FAILED on failure and TM_ECODE_OK on success + */ +TmEcode ReceiveIPFW(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) { + IPFWThreadVars *ptv = (IPFWThreadVars *)data; + char pkt[IP_MAXPACKET]; + int pktlen=0; + int r = 0; + struct pollfd IPFWpoll; + struct timeval IPFWts; + SCEnter(); + + //printf("Entering RecieveIPFW\n"); + + IPFWpoll.fd=ipfw_sock; + IPFWpoll.events= POLLRDNORM; + + /* Read packets from divert socket */ + while (r == 0) { + + /* Did we receive a signal to shutdown */ + if ( TmThreadsCheckFlag(tv, THV_KILL) || TmThreadsCheckFlag(tv, THV_PAUSE)) { + SCLogInfo("Received ThreadShutdown: IPFW divert socket polling interrupted"); + SCReturnInt(TM_ECODE_OK); + } + + /* Poll the socket for status */ + if ( (poll(&IPFWpoll,1,IPFW_SOCKET_POLL_MSEC)) > 0) { + if ( IPFWpoll.revents & (POLLRDNORM | POLLERR) ) + r++; + } + + } /* end while */ + + SCMutexLock(&ipfw_socket_lock); + if ((pktlen = recvfrom(ipfw_sock, pkt, sizeof(pkt), 0,(struct sockaddr *)&ipfw_sin, &ipfw_sinlen)) == -1) { + + /* We received an error on socket read */ + if (errno == EINTR || errno == EWOULDBLOCK) { + /* Nothing for us to process */ + SCMutexUnlock(&ipfw_socket_lock); + SCReturnInt(TM_ECODE_OK); + + } else { + SCLogWarning(SC_WARN_IPFW_RECV,"Read from IPFW divert socket failed: %s",strerror(errno)); + SCMutexUnlock(&ipfw_socket_lock); + SCReturnInt(TM_ECODE_FAILED); + } + + } else { + /* We have a packet to process */ + memset (&IPFWts, 0, sizeof(struct timeval)); + gettimeofday(&IPFWts, NULL); + r++; + } + + SCMutexUnlock(&ipfw_socket_lock); + + SCLogDebug("Received Packet Len: %d",pktlen); + + /* Is the packet queue full, wait if so */ + SCMutexLock(&mutex_pending); + if (pending > MAX_PENDING) { + pthread_cond_wait(&cond_pending, &mutex_pending); + } + SCMutexUnlock(&mutex_pending); + + /* Setup packet */ + p = tv->tmqh_in(tv); + + p->ts.tv_sec = IPFWts.tv_sec; + p->ts.tv_usec = IPFWts.tv_usec; + + ptv->pkts++; + ptv->bytes += pktlen; + + p->datalink = ptv->datalink; + p->pktlen = pktlen; + memcpy(p->pkt, pkt, p->pktlen); + SCLogDebug("Packet info: p->pktlen: %" PRIu32 " (pkt %02x, p->pkt %02x)", p->pktlen, *pkt, *p->pkt); + + /* pass on... */ + tv->tmqh_out(tv, p); + + SCReturnInt(TM_ECODE_OK); + +} + +/** + * \brief Init function for RecieveIPFW. + * + * This is a setup function for recieving packets + * via ipfw divert, binds a socket, and prepares to + * to read from it. + * + * \param tv pointer to ThreadVars + * \param initdata pointer to the divert port passed from the user + * \param data pointer gets populated with IPFWThreadVars + * + */ +TmEcode ReceiveIPFWThreadInit(ThreadVars *tv, void *initdata, void **data) { + + struct timeval timev; + + uint16_t divert_port=0; + char *tmpdivertport; + + sigset_t sigs; + sigfillset(&sigs); + pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); + + SCEnter(); + + /* divert socket port to listen/send on */ + if ( (ConfGet("ipfw-divert-port", &tmpdivertport)) != 1 ) { + SCLogError(SC_ERR_IPFW_NOPORT,"Please supply an IPFW divert port"); + SCReturnInt(TM_ECODE_FAILED); + + } else { + + if (atoi(tmpdivertport) > 0 && atoi(tmpdivertport) <= 65535) { + divert_port = (uint16_t)atoi(tmpdivertport); + SCLogInfo("Using IPFW divert port %u",divert_port); + + } else { + SCLogError(SC_ERR_IPFW_BIND,"Divert port: %s is invalid",tmpdivertport); + SCReturnInt(TM_ECODE_FAILED); + } + } + + /* Setup Threadvars */ + IPFWThreadVars *ptv = malloc(sizeof(IPFWThreadVars)); + if (ptv == NULL) { + SCLogError(SC_ERR_MEM_ALLOC,"Error Allocating memory for IPFW Receive PTV: %s",strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + memset(ptv, 0, sizeof(IPFWThreadVars)); + + SCMutexInit(&ipfw_socket_lock, NULL); + /* We need a divert socket to play with */ + if ((ipfw_sock = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) { + SCLogError(SC_ERR_IPFW_SOCK,"Can't create divert socket: %s", strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + + /* set a timeout to the socket so we can check for a signal + * in case we don't get packets for a longer period. */ + timev.tv_sec = 1; + timev.tv_usec = 0; + + if(setsockopt(ipfw_sock, SOL_SOCKET, SO_RCVTIMEO, &timev, sizeof(timev)) == -1) { + SCLogWarning(SC_WARN_IPFW_SETSOCKOPT,"Can't set IPFW divert socket timeout: %s", strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + + ipfw_sinlen=sizeof(ipfw_sin); + memset(&ipfw_sin, 0, ipfw_sinlen); + ipfw_sin.sin_family = PF_INET; + ipfw_sin.sin_addr.s_addr = INADDR_ANY; + ipfw_sin.sin_port = htons(divert_port); + + /* Bind that SOB */ + if (bind(ipfw_sock, (struct sockaddr *)&ipfw_sin, ipfw_sinlen) == -1) { + SCLogError(SC_ERR_IPFW_BIND,"Can't bind divert socket on port %d: %s",divert_port,strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + + ptv->datalink = DLT_RAW; + + *data = (void *)ptv; + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function prints stats to the screen at exit. + * \todo Unit tests are needed for this module. + * \param tv pointer to ThreadVars + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +void ReceiveIPFWThreadExitStats(ThreadVars *tv, void *data) { + IPFWThreadVars *ptv = (IPFWThreadVars *)data; + + SCEnter(); + + SCLogInfo("(%s) Packets %" PRIu32 ", bytes %" PRIu64 "", tv->name, ptv->pkts, ptv->bytes); + + SCReturn; +} + +/** + * \brief DeInit function closes divert socket at exit. + * \todo Unit tests are needed for this module. + * \param tv pointer to ThreadVars + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +TmEcode ReceiveIPFWThreadDeinit(ThreadVars *tv, void *data) { + IPFWThreadVars *ptv = (IPFWThreadVars *)data; + + SCEnter(); + + /* Attempt to shut the socket down...close instead? */ + if ( (shutdown(ipfw_sock,SHUT_RD)) < 0 ) { + SCLogWarning(SC_WARN_IPFW_UNBIND,"Unable to disable ipfw socket: %s",strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + + data = (void *)ptv; + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function passes off to link type decoders. + * \todo Unit tests are needed for this module. + * + * DecodeIPFW reads packets from the PacketQueue and passes + * them off to the proper link type decoder. + * + * \param tv pointer to ThreadVars + * \param p pointer to the current packet + * \param data pointer that gets cast into IPFWThreadVars for ptv + * \param pq pointer to the PacketQueue + */ +TmEcode DecodeIPFW(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) +{ + IPV4Hdr *ip4h = (IPV4Hdr *)p->pkt; + IPV6Hdr *ip6h = (IPV6Hdr *)p->pkt; + DecodeThreadVars *dtv = (DecodeThreadVars *)data; + + SCEnter(); + + /* update counters */ + SCPerfCounterIncr(dtv->counter_pkts, tv->sc_perf_pca); + SCPerfCounterAddUI64(dtv->counter_bytes, tv->sc_perf_pca, p->pktlen); + SCPerfCounterAddUI64(dtv->counter_avg_pkt_size, tv->sc_perf_pca, p->pktlen); + SCPerfCounterSetUI64(dtv->counter_max_pkt_size, tv->sc_perf_pca, p->pktlen); + + /* Process IP packets */ + if (IPV4_GET_RAW_VER(ip4h) == 4) { + SCLogDebug("DecodeIPFW ip4 processing"); + DecodeIPV4(tv, dtv, p, p->pkt, p->pktlen, pq); + + } else if(IPV6_GET_RAW_VER(ip6h) == 6) { + SCLogDebug("DecodeIPFW ip6 processing"); + DecodeIPV6(tv, dtv, p, p->pkt, p->pktlen, pq); + + } else { + /* We don't support anything besides IP packets for now, bridged packets? */ + SCLogInfo("IPFW unknown protocol support %02x", *p->pkt); + SCReturnInt(TM_ECODE_FAILED); + } + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function initializes the DecodeThreadVariables + * + * + * \param tv pointer to ThreadVars + * \param initdata pointer for passing in args + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +TmEcode DecodeIPFWThreadInit(ThreadVars *tv, void *initdata, void **data) +{ + DecodeThreadVars *dtv = NULL; + + if ( (dtv = malloc(sizeof(DecodeThreadVars))) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC,"Error Allocating memory for IPFW Decode DTV: %s",strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + memset(dtv, 0, sizeof(DecodeThreadVars)); + + DecodeRegisterPerfCounters(dtv, tv); + + *data = (void *)dtv; + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function sets the Verdict and processes the packet + * + * + * \param tv pointer to ThreadVars + * \param p pointer to the Packet + */ +TmEcode IPFWSetVerdict(ThreadVars *tv, IPFWThreadVars *ptv, Packet *p) { + uint32_t verdict; + struct pollfd IPFWpoll; + + SCEnter(); + + IPFWpoll.fd=ipfw_sock; + IPFWpoll.events= POLLWRNORM; + + /* What to do with the packet? */ + switch (p->action) { + case ACTION_ALERT: + case ACTION_PASS: + verdict = IPFW_ACCEPT; + break; + case ACTION_REJECT: + case ACTION_REJECT_DST: + case ACTION_REJECT_BOTH: + case ACTION_DROP: + default: + /* a verdict we don't know about, drop to be sure */ + verdict = IPFW_DROP; + } + + if (verdict == IPFW_ACCEPT) { + SCLogDebug("IPFW Verdict is to Accept"); + ptv->accepted++; + + + /* For divert sockets, accepting means writing the + * packet back to the socket for ipfw to pick up + */ + SCLogDebug("IPFWSetVerdict writing to socket %d, %p, %u", ipfw_sock,p->pkt,p->pktlen); + + + while ( (poll(&IPFWpoll,1,IPFW_SOCKET_POLL_MSEC)) < 1) { + + /* Did we receive a signal to shutdown */ + if (TmThreadsCheckFlag(tv, THV_KILL) || TmThreadsCheckFlag(tv, THV_PAUSE)) { + SCLogInfo("Received ThreadShutdown: IPFW divert socket writing interrupted"); + SCReturnInt(TM_ECODE_OK); + } + } + + SCMutexLock(&ipfw_socket_lock); + if (sendto(ipfw_sock, p->pkt, p->pktlen, 0,(struct sockaddr *)&ipfw_sin, ipfw_sinlen) == -1) { + SCLogWarning(SC_WARN_IPFW_XMIT,"Write to ipfw divert socket failed: %s",strerror(errno)); + SCMutexUnlock(&ipfw_socket_lock); + SCReturnInt(TM_ECODE_FAILED); + } + + SCMutexUnlock(&ipfw_socket_lock); + + SCLogDebug("Sent Packet back into IPFW Len: %d",p->pktlen); + + } /* end IPFW_ACCEPT */ + + + if (verdict == IPFW_DROP) { + SCLogDebug("IPFW SetVerdict is to DROP"); + ptv->dropped++; + + /* For divert sockets, dropping means not writing the packet back to the socket. + * Need to see if there is some better way to free the packet from the queue */ + + } /* end IPFW_DROP */ + + SCReturnInt(TM_ECODE_OK); +} + + +/** + * \brief This function handles the Verdict processing + * \todo Unit tests are needed for this module. + * + * + * \param tv pointer to ThreadVars + * \param p pointer to the Packet + * \param data pointer that gets cast into IPFWThreadVars for ptv + * \param pq pointer for the Packet Queue access (Not used) + */ +TmEcode VerdictIPFW(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq) { + IPFWThreadVars *ptv = (IPFWThreadVars *)data; + TmEcode retval = TM_ECODE_OK; + + SCEnter(); + + /* This came from NFQ. + * if this is a tunnel packet we check if we are ready to verdict + * already. */ + if (IS_TUNNEL_PKT(p)) { + char verdict = 1; + + pthread_mutex_t *m = p->root ? &p->root->mutex_rtv_cnt : &p->mutex_rtv_cnt; + SCMutexLock(m); + /* if there are more tunnel packets than ready to verdict packets, + * we won't verdict this one + */ + if (TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p)) { + SCLogDebug("VerdictIPFW: not ready to verdict yet: TUNNEL_PKT_TPR(p) > TUNNEL_PKT_RTV(p) = %" PRId32 " > %" PRId32 "", TUNNEL_PKT_TPR(p), TUNNEL_PKT_RTV(p)); + verdict = 0; + } + SCMutexUnlock(m); + + /* don't verdict if we are not ready */ + if (verdict == 1) { + SCLogDebug("Setting verdict on tunnel"); + retval=IPFWSetVerdict(tv, ptv, p->root ? p->root : p); + + } else + TUNNEL_INCR_PKT_RTV(p); + + } else { + /* no tunnel, verdict normally */ + SCLogDebug("Setting verdict on non-tunnel"); + retval=IPFWSetVerdict(tv, ptv, p); + + } /* IS_TUNNEL_PKT end */ + + SCReturnInt(retval); +} + +/** + * \brief This function initializes the VerdictThread + * + * + * \param t pointer to ThreadVars + * \param initdata pointer for passing in args + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +TmEcode VerdictIPFWThreadInit(ThreadVars *tv, void *initdata, void **data) { + + IPFWThreadVars *ptv = NULL; + + SCEnter(); + + /* Setup Thread vars */ + if ( (ptv = malloc(sizeof(IPFWThreadVars))) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC,"Error Allocating memory for IPFW Verdict PTV: %s", strerror(errno)); + SCReturnInt(TM_ECODE_FAILED); + } + memset(ptv, 0, sizeof(IPFWThreadVars)); + + + *data = (void *)ptv; + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function deinitializes the VerdictThread + * + * + * \param tv pointer to ThreadVars + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +TmEcode VerdictIPFWThreadDeinit(ThreadVars *tv, void *data) { + + SCEnter(); + + /* We don't need to do anything...not sure quite yet */ + + + SCReturnInt(TM_ECODE_OK); +} + +/** + * \brief This function prints stats for the VerdictThread + * + * + * \param tv pointer to ThreadVars + * \param data pointer that gets cast into IPFWThreadVars for ptv + */ +void VerdictIPFWThreadExitStats(ThreadVars *tv, void *data) { + IPFWThreadVars *ptv = (IPFWThreadVars *)data; + SCLogInfo("IPFW Processing: - (%s) Pkts accepted %" PRIu32 ", dropped %" PRIu32 "", tv->name, ptv->accepted, ptv->dropped); +} + +#endif /* End ifdef IPFW */ + +/* eof */ + diff --git a/src/source-ipfw.h b/src/source-ipfw.h new file mode 100644 index 0000000000..4ca5a7d3e2 --- /dev/null +++ b/src/source-ipfw.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2009 Open Information Security Foundation */ + +/** \file + * \author Nick Rogness + */ + +#ifndef __SOURCE_IPFW_H__ +#define __SOURCE_IPFW_H__ + +#include + + +/* per packet IPFW vars (Not used) */ +typedef struct IPFWPacketVars_ +{ +} IPFWPacketVars; + +void TmModuleReceiveIPFWRegister (void); +void TmModuleVerdictIPFWRegister (void); +void TmModuleDecodeIPFWRegister (void); + +#endif /* __SOURCE_IPFW_H__ */ diff --git a/src/suricata.c b/src/suricata.c index 887f62de93..cfdf307726 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -50,6 +50,8 @@ #include "source-nfq.h" #include "source-nfq-prototypes.h" +#include "source-ipfw.h" + #include "source-pcap.h" #include "source-pcap-file.h" @@ -283,6 +285,7 @@ void usage(const char *progname) printf("\t-i : run in pcap live mode\n"); printf("\t-r : run in pcap file/offline mode\n"); printf("\t-q : run in inline nfqueue mode\n"); + printf("\t-d : run in inline ipfw divert mode\n"); printf("\t-s : path to signature file (optional)\n"); printf("\t-l : default log directory\n"); printf("\t-D : run as daemon\n"); @@ -339,7 +342,7 @@ int main(int argc, char **argv) /* getopt_long stores the option index here. */ int option_index = 0; - char short_opts[] = "c:Dhi:l:q:r:us:U:V"; + char short_opts[] = "c:Dhi:l:q:d:r:us:U:V"; while ((opt = getopt_long(argc, argv, short_opts, long_opts, &option_index)) != -1) { switch (opt) { @@ -415,6 +418,13 @@ int main(int argc, char **argv) run_mode = MODE_NFQ; nfq_id = atoi(optarg); /* strtol? */ break; + case 'd': + run_mode = MODE_IPFW; + if (ConfSet("ipfw-divert-port", optarg, 0) != 1) { + fprintf(stderr, "ERROR: Failed to set ipfw_divert_port\n"); + exit(EXIT_FAILURE); + } + break; case 'r': run_mode = MODE_PCAP_FILE; pcap_file = optarg; @@ -523,6 +533,9 @@ int main(int argc, char **argv) TmModuleReceiveNFQRegister(); TmModuleVerdictNFQRegister(); TmModuleDecodeNFQRegister(); + TmModuleReceiveIPFWRegister(); + TmModuleVerdictIPFWRegister(); + TmModuleDecodeIPFWRegister(); TmModuleReceivePcapRegister(); TmModuleDecodePcapRegister(); TmModuleReceivePfringRegister(); @@ -674,6 +687,9 @@ int main(int argc, char **argv) else if (run_mode == MODE_NFQ) { RunModeIpsNFQ(de_ctx); } + else if (run_mode == MODE_IPFW) { + RunModeIpsIPFW(de_ctx); + } else { SCLogError(SC_ERR_UNKNOWN_RUN_MODE, "Unknown runtime mode. Aborting"); exit(EXIT_FAILURE); diff --git a/src/suricata.h b/src/suricata.h index 00cc8ddec8..f7b2aa52cd 100644 --- a/src/suricata.h +++ b/src/suricata.h @@ -39,6 +39,7 @@ enum { MODE_PCAP_FILE, MODE_PFRING, MODE_NFQ, + MODE_IPFW, MODE_UNITTEST }; diff --git a/src/threads.h b/src/threads.h index 7d098b6dff..0911f6feac 100644 --- a/src/threads.h +++ b/src/threads.h @@ -8,6 +8,10 @@ #ifndef __THREADS_H__ #define __THREADS_H__ +#ifdef OS_FREEBSD +#include +#endif /* OS_FREEBSD */ + #include /** The mutex/spinlock/condition definitions and functions are used @@ -24,6 +28,23 @@ #define SCMutexAttr pthread_mutexattr_t #define SCMutexDestroy pthread_mutex_destroy +/** Get the Current Thread Id */ +#ifdef OS_FREEBSD +#define SCGetThreadIdLong(...) ({ \ + long tmpthid; \ + thr_self(&tmpthid); \ + u_long tid = (u_long)tmpthid; \ + tid; \ +}) +#else +#define SCGetThreadIdLong(...) ({ \ + pid_t tmpthid; \ + tmpthid = syscall(SYS_gettid); \ + u_long tid = (u_long)tmpthid; \ + tid; \ +}) +#endif /* OS FREEBSD */ + /** Mutex Functions */ #ifdef DBG_THREADS /** When dbg threads is defined, if a mutex fail to lock, it's diff --git a/src/tm-modules.h b/src/tm-modules.h index 312a0e46b5..a0537de56f 100644 --- a/src/tm-modules.h +++ b/src/tm-modules.h @@ -46,6 +46,9 @@ enum { TMM_LOGHTTPLOG4, TMM_LOGHTTPLOG6, TMM_STREAMTCP, + TMM_DECODEIPFW, + TMM_VERDICTIPFW, + TMM_RECEIVEIPFW, TMM_SIZE, }; diff --git a/src/tm-threads.c b/src/tm-threads.c index c5422a6ee8..0de18a4653 100644 --- a/src/tm-threads.c +++ b/src/tm-threads.c @@ -20,6 +20,7 @@ #include #include #include +#include #define cpu_set_t cpuset_t #elif OS_DARWIN #include @@ -576,20 +577,20 @@ void TmVarSlotSetFuncAppend(ThreadVars *tv, TmModule *tm, void *data) { /* called from the thread */ static int SetCPUAffinity(int cpu) { - //pthread_t tid = pthread_self(); - pid_t tid = syscall(SYS_gettid); - cpu_set_t cs; - printf("Setting CPU Affinity for thread %" PRIu32 " to CPU %" PRId32 "\n", tid, cpu); + printf("Setting CPU Affinity for thread %lu to CPU %" PRId32 "\n", SCGetThreadIdLong(), cpu); + + cpu_set_t cs; CPU_ZERO(&cs); CPU_SET(cpu,&cs); #ifdef OS_FREEBSD - int r = cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_TID,tid,sizeof(cpu_set_t),&cs); + int r = cpuset_setaffinity(CPU_LEVEL_WHICH,CPU_WHICH_TID,SCGetThreadIdLong(),sizeof(cpu_set_t),&cs); #elif OS_DARWIN int r = thread_policy_set(mach_thread_self(), THREAD_AFFINITY_POLICY, (void*)&cs, THREAD_AFFINITY_POLICY_COUNT); #else + pid_t tid = syscall(SYS_gettid); int r = sched_setaffinity(tid,sizeof(cpu_set_t),&cs); #endif /* OS_FREEBSD */ diff --git a/src/util-debug-filters.h b/src/util-debug-filters.h index bdd70e287c..0bd1661df9 100644 --- a/src/util-debug-filters.h +++ b/src/util-debug-filters.h @@ -8,19 +8,6 @@ #include #include "threads.h" -/** - * \brief extra defines needed for FreeBSD - */ -#ifdef OS_FREEBSD -#ifndef SYS_gettid -#if __i386__ -#define SYS_gettid 224 -#elif __amd64__ -#define SYS_gettid 186 -#endif /* cpu arch */ -#endif /* SYS_gettid */ -#endif /* OS_FREEBSD */ - /** * \brief Enum that holds the different kinds of filters available */ diff --git a/src/util-debug.c b/src/util-debug.c index 53b387de7f..98e5a57bfe 100644 --- a/src/util-debug.c +++ b/src/util-debug.c @@ -11,7 +11,7 @@ #include #include #include - +#include "threads.h" #include "util-debug.h" #include "util-error.h" #include "util-enum.h" @@ -334,7 +334,7 @@ SCError SCLogMessage(SCLogLevel log_level, char **msg, const char *file, case SC_LOG_FMT_TID: temp_fmt[0] = '\0'; cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - *msg), - "%s%lu", substr, syscall(SYS_gettid)); + "%s%lu", substr, SCGetThreadIdLong()); if (cw < 0) goto error; temp += cw; diff --git a/src/util-error.c b/src/util-error.c index bec4056fcf..c6c180c1a6 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -77,6 +77,14 @@ const char * SCErrorToString(SCError err) CASE_CODE (SC_ERR_MISSING_QUOTE); CASE_CODE (SC_ERR_UNKNOWN_PROTOCOL); CASE_CODE (SC_ERR_UNKNOWN_RUN_MODE); + CASE_CODE (SC_ERR_IPFW_NOSUPPORT); + CASE_CODE (SC_ERR_IPFW_BIND); + CASE_CODE (SC_ERR_IPFW_SOCK); + CASE_CODE (SC_ERR_IPFW_NOPORT); + CASE_CODE (SC_WARN_IPFW_RECV); + CASE_CODE (SC_WARN_IPFW_XMIT); + CASE_CODE (SC_WARN_IPFW_SETSOCKOPT); + CASE_CODE (SC_WARN_IPFW_UNBIND); default: return "UNKNOWN_ERROR"; diff --git a/src/util-error.h b/src/util-error.h index f9d7e125d8..3363ffba36 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -67,6 +67,14 @@ typedef enum { SC_NFQ_HANDLE_PKT, SC_NFQ_SET_VERDICT, SC_NFQ_THREAD_INIT, + SC_ERR_IPFW_NOSUPPORT, + SC_ERR_IPFW_BIND, + SC_ERR_IPFW_SOCK, + SC_ERR_IPFW_NOPORT, + SC_WARN_IPFW_RECV, + SC_WARN_IPFW_XMIT, + SC_WARN_IPFW_SETSOCKOPT, + SC_WARN_IPFW_UNBIND, SC_ERR_DAEMON, SC_UNIMPLEMENTED, SC_ERR_ADDRESS_ENGINE_GENERIC_ERROR, diff --git a/suricata.yaml b/suricata.yaml index 3f9bd0fc86..12a39b37dc 100644 --- a/suricata.yaml +++ b/suricata.yaml @@ -149,8 +149,34 @@ pfring: # clusterid. clusterid: 99 - #Set the default rule path here to search for the files. - #if not set, it will look at the current working dir +# For FreeBSD ipfw(8) divert(4) support. +# Please make sure you have ipfw_load="YES" and ipdivert_load="YES" +# in /etc/loader.conf or kldload'ing the appropriate kernel modules. +# Additionally, you need to have an ipfw rule for the engine to see +# the packets from ipfw. For Example: +# +# ipfw add 100 divert 8000 ip from any to any +# +# The 8000 above should be the same number you passed on the command +# line, i.e. -d 8000 +# +ipfw: + + # Reinject packets at the specified ipfw rule number. This config + # option is the ipfw rule number AT WHICH rule processing continues + # in the ipfw processing system after the engine has finished + # inspecting the packet for acceptance. If no rule number is specified, + # accepted packets are reinjected at the divert rule which they entered + # and IPFW rule processing continues. No check is done to verify + # this will rule makes sense so care must be taken to avoid loops in ipfw. + # + ## The following example tells the engine to reinject packets + # back into the ipfw firewall AT rule number 5500: + # + # ipfw-reinjection-rule-number: 5500 + +# Set the default rule path here to search for the files. +# if not set, it will look at the current working dir default-rule-path: /etc/suricata/rules/ rule-files: - attack-responses.rules