]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
af-packet: IPS and TAP feature
authorEric Leblond <eric@regit.org>
Thu, 19 Jul 2012 18:07:05 +0000 (20:07 +0200)
committerVictor Julien <victor@inliniac.net>
Mon, 3 Sep 2012 13:27:38 +0000 (15:27 +0200)
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
src/source-af-packet.c
src/source-af-packet.h
src/suricata.c
suricata.yaml.in

index d9309a4ec468e7cc860f2498c09092b00738c9fc..d2922e4fa6d033875744675c702f27bba7c17113 100644 (file)
@@ -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", &copymodestr) == 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 */
index 6fa02c59838e7af017a64d6584a2bc78084bd115..2aaa26eb7f14995e0516f19512b2be475e2e1e14 100644 (file)
@@ -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);
 }
 
index 97dc2c29c731a367835f84b2e83f846856bd6e14..43bb21df5b8be1c3bebe9d937c364c67e59ee986 100644 (file)
@@ -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
 #define PACKET_FANOUT_LB               1
 #define PACKET_FANOUT_CPU              2
 #define PACKET_FANOUT_FLAG_DEFRAG      0x8000
-
 #else /* HAVE_PACKET_FANOUT */
 #include <linux/if_packet.h>
 #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__ */
index 13fa3d39d6e3c6349c701342b1e518fb6d36fb76..521dc3ce75f212290ae87a68226656e7bef6ac06 100644 (file)
@@ -2015,6 +2015,8 @@ int main(int argc, char **argv)
     TmqhCleanup();
     TmModuleRunDeInit();
 
+    AFPPeersListClean();
+
 #ifdef PROFILING
     if (profiling_rules_enabled)
         SCProfilingDump();
index 4fc9ef6dc384976cf7e93de09d978e72a9ff0596..224bd349aa763e0a39d8801fecbfed3b07fd9ae8 100644 (file)
@@ -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