From 662dccd8a5180807e3749842508b80e2e2183051 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 19 Jul 2012 20:07:05 +0200 Subject: [PATCH] af-packet: IPS and TAP feature This patch adds a new feature to AF_PACKET capture mode. It is now possible to use AF_PACKET in IPS and TAP mode: all traffic received on a interface will be forwarded (at the Ethernet level) to an other interface. To do so, Suricata create a raw socket and sends the receive packets to a interface designed in the configuration file. This patch adds two variables to the configuration of af-packet interface: copy-mode: ips or tap copy-iface: eth1 #the interface where packet are copied If copy-mode is set to ips then the packet wth action DROP are not copied to the destination interface. If copy-mode is set to tap, all packets are copied to the destination interface. Any other value of copy-mode results in the feature to be unused. There is no default interface for copy-iface and the variable has to be set for the ids or tap mode to work. For now, this feature depends of the release data system. This implies you need to activate the ring mode and zero copy. Basically use-mmap has to be set to yes. This patch adds a peering of AF_PACKET sockets from the thread on one interface to the threads on another interface. Peering is necessary as if we use an other socket the capture socket receives all emitted packets. This is made using a new AFPPeer structure to avoid direct interaction between AFPTreadVars. There is currently a bug in Linux kernel (prior to 3.6) and it is not possible to use multiple threads. You need to setup two interfaces with equality on the threads variable. copy-mode variable must be set on the two interfaces and use-mmap must be set to activated. A valid configuration for an IPS using eth0 and vboxnet1 interfaces will look like: af-packet: - interface: eth0 threads: 1 defrag: yes cluster-type: cluster_flow cluster-id: 98 copy-mode: ips copy-iface: vboxnet1 buffer-size: 64535 use-mmap: yes - interface: vboxnet1 threads: 1 cluster-id: 97 defrag: yes cluster-type: cluster_flow copy-mode: ips copy-iface: eth0 buffer-size: 64535 use-mmap: yes --- src/runmode-af-packet.c | 93 ++++++++++++++-- src/source-af-packet.c | 236 ++++++++++++++++++++++++++++++++++++++-- src/source-af-packet.h | 29 ++++- src/suricata.c | 2 + suricata.yaml.in | 7 ++ 5 files changed, 346 insertions(+), 21 deletions(-) diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index d9309a4ec4..d2922e4fa6 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -109,9 +109,11 @@ void *ParseAFPConfig(const char *iface) AFPIfaceConfig *aconf = SCMalloc(sizeof(*aconf)); char *tmpclusterid; char *tmpctype; + char *copymodestr; intmax_t value; int boolval; char *bpf_filter = NULL; + char *out_iface = NULL; if (aconf == NULL) { return NULL; @@ -134,6 +136,7 @@ void *ParseAFPConfig(const char *iface) aconf->DerefFunc = AFPDerefConfig; aconf->flags = 0; aconf->bpf_filter = NULL; + aconf->out_iface = NULL; if (ConfGet("bpf-filter", &bpf_filter) == 1) { if (strlen(bpf_filter) > 0) { @@ -169,6 +172,44 @@ void *ParseAFPConfig(const char *iface) aconf->threads = 1; } + if (ConfGetChildValue(if_root, "copy-iface", &out_iface) == 1) { + if (strlen(out_iface) > 0) { + aconf->out_iface = out_iface; + } + } + + (void)ConfGetChildValueBool(if_root, "use-mmap", (int *)&boolval); + if (boolval) { + SCLogInfo("Enabling mmaped capture on iface %s", + aconf->iface); + aconf->flags |= AFP_RING_MODE; + } + + aconf->copy_mode = AFP_COPY_MODE_NONE; + if (ConfGetChildValue(if_root, "copy-mode", ©modestr) == 1) { + if (aconf->out_iface == NULL) { + SCLogInfo("Copy mode activated but no destination" + " iface. Disabling feature"); + } else if (!(aconf->flags & AFP_RING_MODE)) { + SCLogInfo("Copy mode activated but use-mmap " + "set to no. Disabling feature"); + } else if (strlen(copymodestr) <= 0) { + aconf->out_iface = NULL; + } else if (strcmp(copymodestr, "ips") == 0) { + SCLogInfo("AF_PACKET IPS mode activated %s->%s", + iface, + aconf->out_iface); + aconf->copy_mode = AFP_COPY_MODE_IPS; + } else if (strcmp(copymodestr, "tap") == 0) { + SCLogInfo("AF_PACKET TAP mode activated %s->%s", + iface, + aconf->out_iface); + aconf->copy_mode = AFP_COPY_MODE_TAP; + } else { + SCLogInfo("Invalid mode (not in tap, ips)"); + } + } + SC_ATOMIC_RESET(aconf->ref); (void) SC_ATOMIC_ADD(aconf->ref, aconf->threads); @@ -246,13 +287,6 @@ void *ParseAFPConfig(const char *iface) aconf->iface); aconf->promisc = 0; } - (void)ConfGetChildValueBool(if_root, "use-mmap", (int *)&boolval); - if (boolval) { - SCLogInfo("Enabling mmaped capture on iface %s", - aconf->iface); - aconf->flags |= AFP_RING_MODE; - } - if (ConfGetChildValue(if_root, "checksum-checks", &tmpctype) == 1) { if (strcmp(tmpctype, "auto") == 0) { @@ -309,6 +343,11 @@ int RunModeIdsAFPAuto(DetectEngineCtx *de_ctx) (void)ConfGet("af-packet.live-interface", &live_dev); + if (AFPPeersListInit() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Unable to init peers list."); + exit(EXIT_FAILURE); + } + ret = RunModeSetLiveCaptureAuto(de_ctx, ParseAFPConfig, AFPConfigGeThreadsCount, @@ -320,6 +359,12 @@ int RunModeIdsAFPAuto(DetectEngineCtx *de_ctx) exit(EXIT_FAILURE); } + /* In IPS mode each threads must have a peer */ + if (AFPPeersListCheck() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer."); + exit(EXIT_FAILURE); + } + SCLogInfo("RunModeIdsAFPAuto initialised"); #endif SCReturnInt(0); @@ -342,6 +387,11 @@ int RunModeIdsAFPAutoFp(DetectEngineCtx *de_ctx) SCLogDebug("live_dev %s", live_dev); + if (AFPPeersListInit() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Unable to init peers list."); + exit(EXIT_FAILURE); + } + ret = RunModeSetLiveCaptureAutoFp(de_ctx, ParseAFPConfig, AFPConfigGeThreadsCount, @@ -353,8 +403,13 @@ int RunModeIdsAFPAutoFp(DetectEngineCtx *de_ctx) exit(EXIT_FAILURE); } - SCLogInfo("RunModeIdsAFPAutoFp initialised"); + /* In IPS mode each threads must have a peer */ + if (AFPPeersListCheck() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer."); + exit(EXIT_FAILURE); + } + SCLogInfo("RunModeIdsAFPAutoFp initialised"); #endif /* HAVE_AF_PACKET */ SCReturnInt(0); @@ -377,6 +432,11 @@ int RunModeIdsAFPSingle(DetectEngineCtx *de_ctx) (void)ConfGet("af-packet.live-interface", &live_dev); + if (AFPPeersListInit() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Unable to init peers list."); + exit(EXIT_FAILURE); + } + ret = RunModeSetLiveCaptureSingle(de_ctx, ParseAFPConfig, AFPConfigGeThreadsCount, @@ -388,6 +448,12 @@ int RunModeIdsAFPSingle(DetectEngineCtx *de_ctx) exit(EXIT_FAILURE); } + /* In IPS mode each threads must have a peer */ + if (AFPPeersListCheck() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer."); + exit(EXIT_FAILURE); + } + SCLogInfo("RunModeIdsAFPSingle initialised"); #endif /* HAVE_AF_PACKET */ @@ -414,6 +480,11 @@ int RunModeIdsAFPWorkers(DetectEngineCtx *de_ctx) (void)ConfGet("af-packet.live-interface", &live_dev); + if (AFPPeersListInit() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Unable to init peers list."); + exit(EXIT_FAILURE); + } + ret = RunModeSetLiveCaptureWorkers(de_ctx, ParseAFPConfig, AFPConfigGeThreadsCount, @@ -425,6 +496,12 @@ int RunModeIdsAFPWorkers(DetectEngineCtx *de_ctx) exit(EXIT_FAILURE); } + /* In IPS mode each threads must have a peer */ + if (AFPPeersListCheck() != TM_ECODE_OK) { + SCLogError(SC_ERR_RUNMODE, "Some IPS capture threads did not peer."); + exit(EXIT_FAILURE); + } + SCLogInfo("RunModeIdsAFPSingle initialised"); #endif /* HAVE_AF_PACKET */ diff --git a/src/source-af-packet.c b/src/source-af-packet.c index 6fa02c5983..2aaa26eb7f 100644 --- a/src/source-af-packet.c +++ b/src/source-af-packet.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Open Information Security Foundation +/* Copyright (C) 2011,2012 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -166,6 +166,10 @@ typedef struct AFPThreadVars_ int promisc; ChecksumValidationMode checksum_mode; + /* IPS stuff */ + char out_iface[AFP_IFACE_NAME_LENGTH]; + AFPPeer *mpeer; + int flags; uint16_t capture_kernel_packets; uint16_t capture_kernel_drops; @@ -174,6 +178,7 @@ typedef struct AFPThreadVars_ int cluster_type; int threads; + int copy_mode; struct tpacket_req req; unsigned int tp_hdrlen; @@ -182,6 +187,7 @@ typedef struct AFPThreadVars_ char *frame_buf; unsigned int frame_offset; int ring_size; + } AFPThreadVars; TmEcode ReceiveAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); @@ -194,6 +200,7 @@ TmEcode DecodeAFPThreadInit(ThreadVars *, void *, void **); TmEcode DecodeAFP(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); TmEcode AFPSetBPFFilter(AFPThreadVars *ptv); +static int AFPGetIfnumByDev(int fd, const char *ifname, int verbose); /** * \brief Registration Function for RecieveAFP. @@ -211,6 +218,119 @@ void TmModuleReceiveAFPRegister (void) { tmm_modules[TMM_RECEIVEAFP].flags = TM_FLAG_RECEIVE_TM; } +typedef struct AFPPeersList_ { + TAILQ_HEAD(, AFPPeer_) peers; /**< Head of list of fragments. */ + int cnt; + int peered; +} AFPPeersList; + +/** + * Update the peer. + */ +void AFPPeerUpdate(AFPThreadVars *ptv) +{ + if (ptv->mpeer == NULL) { + return; + } + SCLogInfo("Updating AFP peer"); + (void)SC_ATOMIC_SET(ptv->mpeer->if_idx, AFPGetIfnumByDev(ptv->socket, ptv->iface, 0)); + (void)SC_ATOMIC_SET(ptv->mpeer->socket, ptv->socket); + (void)SC_ATOMIC_SET(ptv->mpeer->state, ptv->afp_state); +} + +void AFPPeerClean(AFPPeer *peer) +{ + if (peer->flags & AFP_SOCK_PROTECT) + SCMutexDestroy(&peer->sock_protect); + SC_ATOMIC_DESTROY(peer->socket); + SC_ATOMIC_DESTROY(peer->if_idx); + SC_ATOMIC_DESTROY(peer->state); + SCFree(peer); +} + +AFPPeersList peerslist; + + +TmEcode AFPPeersListInit() +{ + SCEnter(); + TAILQ_INIT(&peerslist.peers); + peerslist.peered = 0; + peerslist.cnt = 0; + SCReturnInt(TM_ECODE_OK); +} + +TmEcode AFPPeersListCheck() +{ +#define AFP_PEERS_MAX_TRY 4 +#define AFP_PEERS_WAIT 20000 + int try = 0; + SCEnter(); + while (try < AFP_PEERS_MAX_TRY) { + if (peerslist.cnt != peerslist.peered) { + usleep(AFP_PEERS_WAIT); + } else { + SCReturnInt(TM_ECODE_OK); + } + try++; + } + SCLogError(SC_ERR_AFP_CREATE, "Threads number not equals"); + SCReturnInt(TM_ECODE_FAILED); +} + +TmEcode AFPPeersListAdd(AFPThreadVars *ptv) +{ + SCEnter(); + AFPPeer *peer = SCMalloc(sizeof(AFPPeer)); + AFPPeer *pitem; + + if (peer == NULL) { + SCReturnInt(TM_ECODE_FAILED); + } + memset(peer, 0, sizeof(AFPPeer)); + SC_ATOMIC_INIT(peer->socket); + SC_ATOMIC_INIT(peer->if_idx); + SC_ATOMIC_INIT(peer->state); + peer->flags = ptv->flags; + + if (peer->flags & AFP_SOCK_PROTECT) { + SCMutexInit(&peer->sock_protect, NULL); + } + + (void)SC_ATOMIC_SET(peer->state, AFP_STATE_DOWN); + strlcpy(peer->iface, ptv->iface, AFP_IFACE_NAME_LENGTH); + ptv->mpeer = peer; + /* add element to iface list */ + TAILQ_INSERT_TAIL(&peerslist.peers, peer, next); + peerslist.cnt++; + + /* Iter to find a peer */ + TAILQ_FOREACH(pitem, &peerslist.peers, next) { + if (pitem->peer) + continue; + if (strcmp(pitem->iface, ptv->out_iface)) + continue; + peer->peer = pitem; + pitem->peer = peer; + peerslist.peered += 2; + break; + } + + AFPPeerUpdate(ptv); + + SCReturnInt(TM_ECODE_OK); +} + +void AFPPeersListClean() +{ + AFPPeer *pitem; + + while ((pitem = TAILQ_FIRST(&peerslist.peers))) { + TAILQ_REMOVE(&peerslist.peers, pitem, next); + AFPPeerClean(pitem); + } +} + /** * \brief Registration Function for DecodeAFP. * \todo Unit tests are needed for this module. @@ -226,6 +346,7 @@ void TmModuleDecodeAFPRegister (void) { tmm_modules[TMM_DECODEAFP].flags = TM_FLAG_DECODE_TM; } + static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose); static inline void AFPDumpCounters(AFPThreadVars *ptv, int forced) @@ -368,13 +489,64 @@ int AFPRead(AFPThreadVars *ptv) SCReturnInt(AFP_READ_OK); } +TmEcode AFPWritePacket(Packet *p) +{ + struct sockaddr_ll socket_address; + int socket; + + if (p->afp_v.copy_mode == AFP_COPY_MODE_IPS) { + if (p->action & ACTION_DROP) { + return TM_ECODE_OK; + } + } + + if (SC_ATOMIC_GET(p->afp_v.peer->state) == AFP_STATE_DOWN) + return TM_ECODE_OK; + + if (p->ethh == NULL) { + SCLogWarning(SC_ERR_INVALID_VALUE, "Should have an Ethernet header"); + return TM_ECODE_FAILED; + } + /* Index of the network device */ + socket_address.sll_ifindex = SC_ATOMIC_GET(p->afp_v.peer->if_idx); + /* Address length*/ + socket_address.sll_halen = ETH_ALEN; + /* Destination MAC */ + memcpy(socket_address.sll_addr, p->ethh, 6); + + /* Send packet, locking the socket if necessary */ + if (p->afp_v.peer->flags & AFP_SOCK_PROTECT) + SCMutexLock(&p->afp_v.peer->sock_protect); + socket = SC_ATOMIC_GET(p->afp_v.peer->socket); + if (sendto(socket, GET_PKT_DATA(p), GET_PKT_LEN(p), 0, + (struct sockaddr*) &socket_address, + sizeof(struct sockaddr_ll)) < 0) { + SCLogWarning(SC_ERR_SOCKET, "Sending packet failed on socket %d: %s", + socket, + strerror(errno)); + if (p->afp_v.peer->flags & AFP_SOCK_PROTECT) + SCMutexUnlock(&p->afp_v.peer->sock_protect); + return TM_ECODE_FAILED; + } + if (p->afp_v.peer->flags & AFP_SOCK_PROTECT) + SCMutexUnlock(&p->afp_v.peer->sock_protect); + + return TM_ECODE_OK; +} + TmEcode AFPReleaseDataFromRing(ThreadVars *t, Packet *p) { + int ret = TM_ECODE_OK; + /* Need to be in copy mode and need to detect early release + where Ethernet header could not be set (and pseudo packet) */ + if ((p->afp_v.copy_mode != AFP_COPY_MODE_NONE) && !PKT_IS_PSEUDOPKT(p)) { + ret = AFPWritePacket(p); + } if (p->afp_v.relptr) { union thdr h; h.raw = p->afp_v.relptr; h.h2->tp_status = TP_STATUS_KERNEL; - return TM_ECODE_OK; + return ret; } return TM_ECODE_FAILED; } @@ -392,6 +564,7 @@ int AFPReadFromRing(AFPThreadVars *ptv) { Packet *p = NULL; union thdr h; + struct sockaddr_ll *from; /* Loop till we have packets available */ while (1) { @@ -399,12 +572,8 @@ int AFPReadFromRing(AFPThreadVars *ptv) h.raw = (((union thdr **)ptv->frame_buf)[ptv->frame_offset]); if (h.raw == NULL) { SCReturnInt(AFP_FAILURE); - } else { - if (ptv->flags & AFP_RING_MODE) { - p->afp_v.relptr = h.raw; - p->ReleaseData = AFPReleaseDataFromRing; - } } + if (h.h2->tp_status == TP_STATUS_KERNEL) { SCReturnInt(AFP_READ_OK); } @@ -414,6 +583,20 @@ int AFPReadFromRing(AFPThreadVars *ptv) SCReturnInt(AFP_FAILURE); } + if (ptv->flags & AFP_RING_MODE) { + p->afp_v.relptr = h.raw; + p->ReleaseData = AFPReleaseDataFromRing; + + p->afp_v.copy_mode = ptv->copy_mode; + if (p->afp_v.copy_mode != AFP_COPY_MODE_NONE) { + p->afp_v.peer = ptv->mpeer->peer; + } else { + p->afp_v.peer = NULL; + } + } + + from = (void *)h.raw + TPACKET_ALIGN(ptv->tp_hdrlen); + ptv->pkts++; ptv->bytes += h.h2->tp_len; (void) SC_ATOMIC_ADD(ptv->livedev->pkts, 1); @@ -422,7 +605,6 @@ int AFPReadFromRing(AFPThreadVars *ptv) /* add forged header */ if (ptv->cooked) { SllHdr * hdrp = (SllHdr *)ptv->data; - struct sockaddr_ll *from = (void *)h.raw + TPACKET_ALIGN(ptv->tp_hdrlen); /* XXX this is minimalist, but this seems enough */ hdrp->sll_protocol = from->sll_protocol; } @@ -436,6 +618,18 @@ int AFPReadFromRing(AFPThreadVars *ptv) if (PacketSetData(p, (unsigned char*)h.raw + h.h2->tp_mac, h.h2->tp_snaplen) == -1) { TmqhOutputPacketpool(ptv->tv, p); SCReturnInt(AFP_FAILURE); + } else { + if (ptv->flags & AFP_RING_MODE) { + p->afp_v.relptr = h.raw; + + p->afp_v.copy_mode = ptv->copy_mode; + if (p->afp_v.copy_mode != AFP_COPY_MODE_NONE) { + p->afp_v.peer = ptv->mpeer->peer; + } else { + p->afp_v.peer = NULL; + } + p->ReleaseData = AFPReleaseDataFromRing; + } } } else { if (PacketCopyData(p, (unsigned char*)h.raw + h.h2->tp_mac, h.h2->tp_snaplen) == -1) { @@ -499,7 +693,6 @@ static int AFPTryReopen(AFPThreadVars *ptv) } SCLogInfo("Recovering interface listening"); - ptv->afp_state = AFP_STATE_UP; return 0; } @@ -525,6 +718,9 @@ TmEcode ReceiveAFPLoop(ThreadVars *tv, void *data, void *slot) /* Start by checking the state of our interface */ if (unlikely(ptv->afp_state == AFP_STATE_DOWN)) { int dbreak = 0; + + AFPPeerUpdate(ptv); + do { usleep(AFP_RECONNECT_TIMEOUT); if (suricata_ctl_flags != 0) { @@ -702,6 +898,7 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) struct sockaddr_ll bind_address; int order; unsigned int i; + int if_idx; /* open socket */ ptv->socket = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); @@ -709,12 +906,13 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) SCLogError(SC_ERR_AFP_CREATE, "Couldn't create a AF_PACKET socket, error %s", strerror(errno)); return -1; } - SCLogDebug("using interface %s", (char *)devname); + SCLogInfo("Using interface '%s' via socket %d", (char *)devname, ptv->socket); + if_idx = AFPGetIfnumByDev(ptv->socket, devname, verbose); /* bind socket */ memset(&bind_address, 0, sizeof(bind_address)); bind_address.sll_family = AF_PACKET; bind_address.sll_protocol = htons(ETH_P_ALL); - bind_address.sll_ifindex = AFPGetIfnumByDev(ptv->socket, devname, verbose); + bind_address.sll_ifindex = if_idx; if (bind_address.sll_ifindex == -1) { if (verbose) SCLogError(SC_ERR_AFP_CREATE, "Couldn't find iface %s", devname); @@ -898,6 +1096,8 @@ static int AFPCreateSocket(AFPThreadVars *ptv, char *devname, int verbose) /* Init is ok */ ptv->afp_state = AFP_STATE_UP; + + AFPPeerUpdate(ptv); return 0; } @@ -1027,6 +1227,9 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { if (active_runmode && !strcmp("workers", active_runmode)) { ptv->flags |= AFP_ZERO_COPY; SCLogInfo("Enabling zero copy mode"); + } else { + /* If we are using copy mode we need a lock */ + ptv->flags |= AFP_SOCK_PROTECT; } /* If we are in RING mode, then we can use ZERO copy @@ -1044,6 +1247,16 @@ TmEcode ReceiveAFPThreadInit(ThreadVars *tv, void *initdata, void **data) { SCReturnInt(TM_ECODE_FAILED); } + ptv->copy_mode = afpconfig->copy_mode; + if (ptv->copy_mode != AFP_COPY_MODE_NONE) { + strlcpy(ptv->out_iface, afpconfig->out_iface, AFP_IFACE_NAME_LENGTH); + ptv->out_iface[AFP_IFACE_NAME_LENGTH - 1]= '\0'; + if (AFPPeersListAdd(ptv) == TM_ECODE_FAILED) { + SCFree(ptv); + afpconfig->DerefFunc(afpconfig); + SCReturnInt(TM_ECODE_FAILED); + } + } #define T_DATA_SIZE 70000 @@ -1099,6 +1312,7 @@ TmEcode ReceiveAFPThreadDeinit(ThreadVars *tv, void *data) { ptv->bpf_filter = NULL; close(ptv->socket); + SCReturnInt(TM_ECODE_OK); } diff --git a/src/source-af-packet.h b/src/source-af-packet.h index 97dc2c29c7..43bb21df5b 100644 --- a/src/source-af-packet.h +++ b/src/source-af-packet.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Open Information Security Foundation +/* Copyright (C) 2011,2012 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -33,14 +33,19 @@ #define PACKET_FANOUT_LB 1 #define PACKET_FANOUT_CPU 2 #define PACKET_FANOUT_FLAG_DEFRAG 0x8000 - #else /* HAVE_PACKET_FANOUT */ #include #endif /* HAVE_PACKET_FANOUT */ +#include "queue.h" /* value for flags */ #define AFP_RING_MODE (1<<0) #define AFP_ZERO_COPY (1<<1) +#define AFP_SOCK_PROTECT (1<<2) + +#define AFP_COPY_MODE_NONE 0 +#define AFP_COPY_MODE_TAP 1 +#define AFP_COPY_MODE_IPS 2 #define AFP_FILE_MAX_PKTS 256 #define AFP_IFACE_NAME_LENGTH 48 @@ -61,20 +66,40 @@ typedef struct AFPIfaceConfig_ int promisc; /* misc use flags including ring mode */ int flags; + int copy_mode; ChecksumValidationMode checksum_mode; char *bpf_filter; + char *out_iface; SC_ATOMIC_DECLARE(unsigned int, ref); void (*DerefFunc)(void *); } AFPIfaceConfig; +typedef struct AFPPeer_ { + char iface[AFP_IFACE_NAME_LENGTH]; + SC_ATOMIC_DECLARE(int, socket); + SC_ATOMIC_DECLARE(int, if_idx); + SC_ATOMIC_DECLARE(uint8_t, state); + SCMutex sock_protect; + int flags; + struct AFPPeer_ *peer; + TAILQ_ENTRY(AFPPeer_) next; +} AFPPeer; + /* per packet AF_PACKET vars */ typedef struct AFPPacketVars_ { void *relptr; + int copy_mode; + AFPPeer *peer; } AFPPacketVars; void TmModuleReceiveAFPRegister (void); void TmModuleDecodeAFPRegister (void); +TmEcode AFPPeersListInit(); +TmEcode AFPPeersListCheck(); +void AFPPeersListClean(); + + #endif /* __SOURCE_AFP_H__ */ diff --git a/src/suricata.c b/src/suricata.c index 13fa3d39d6..521dc3ce75 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2015,6 +2015,8 @@ int main(int argc, char **argv) TmqhCleanup(); TmModuleRunDeInit(); + AFPPeersListClean(); + #ifdef PROFILING if (profiling_rules_enabled) SCProfilingDump(); diff --git a/suricata.yaml.in b/suricata.yaml.in index 4fc9ef6dc3..224bd349aa 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -253,6 +253,13 @@ af-packet: #checksum-checks: kernel # BPF filter to apply to this interface. The pcap filter syntax apply here. #bpf-filter: port 80 or udp + # You can use the following variables to activate AF_PACKET tap od IPS mode. + # If copy-mode is set to ips or tap, the traffic coming to the current + # interface will be copied to the copy-iface interface. If 'tap' is set, the + # copy is complete. If 'ips' is set, the packet matching a 'drop' action + # will not be copied. + #copy-mode: ips + #copy-iface: eth1 - interface: eth1 threads: 1 cluster-id: 98