summaryrefslogtreecommitdiffstats
path: root/contrib/impcap/impcap.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/impcap/impcap.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/contrib/impcap/impcap.c b/contrib/impcap/impcap.c
new file mode 100644
index 0000000..cdb1e54
--- /dev/null
+++ b/contrib/impcap/impcap.c
@@ -0,0 +1,748 @@
+/* impcap.c
+ *
+ * This is an input module using libpcap, a
+ * portable C/C++ library for network traffic capture.
+ * This module reads packets received from a network interface
+ * using libpcap, to extract information such as IP addresses, ports,
+ * protocols, etc... and make it available to rsyslog and other modules.
+ *
+ * File begun on 2018-11-13
+ *
+ * Created by:
+ * - Théo Bertin (theo.bertin@advens.fr)
+ *
+ * With:
+ * - François Bernard (francois.bernard@isen.yncrea.fr)
+ * - Tianyu Geng (tianyu.geng@isen.yncrea.fr)
+ *
+ * This file is part of rsyslog.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * -or-
+ * see COPYING.ASL20 in the source distribution
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <signal.h>
+#include <json.h>
+
+#include <pcap.h>
+
+#include "rsyslog.h"
+#include "prop.h"
+#include "ruleset.h"
+#include "datetime.h"
+
+#include "errmsg.h"
+#include "unicode-helper.h"
+#include "module-template.h"
+#include "rainerscript.h"
+#include "rsconf.h"
+#include "glbl.h"
+#include "srUtils.h"
+
+#include "parsers.h"
+
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+MODULE_CNFNAME("impcap")
+
+#define DEFAULT_META_CONTAINER "!impcap"
+#define DEFAULT_DATA_CONTAINER "!data"
+
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(ruleset)
+DEFobjCurrIf(datetime)
+
+static prop_t *pInputName = NULL;
+
+char *stringToHex(char *string, size_t length);
+
+static ATTR_NORETURN void *startCaptureThread(void *instanceConf);
+
+/* conf structures */
+
+struct instanceConf_s {
+ char *interface;
+ uchar *filePath;
+ pcap_t *device;
+ uchar *filter;
+ uchar *tag;
+ uint8_t promiscuous;
+ uint8_t immediateMode;
+ uint32_t bufSize;
+ uint8_t bufTimeout;
+ uint8_t pktBatchCnt;
+ pthread_t tid;
+ uchar *pszBindRuleset; /* name of ruleset to bind to */
+ ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */
+ struct instanceConf_s *next;
+};
+
+struct modConfData_s {
+ rsconf_t *pConf;
+ instanceConf_t *root, *tail;
+ uint16_t snap_length;
+ uint8_t metadataOnly;
+ char *metadataContainer;
+ char *dataContainer;
+};
+
+static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
+static modConfData_t *runModConf = NULL; /* modConf ptr to use for the current exec process */
+
+/* input instance parameters */
+static struct cnfparamdescr inppdescr[] = {
+ {"interface", eCmdHdlrGetWord, 0},
+ {"file", eCmdHdlrString, 0},
+ {"promiscuous", eCmdHdlrBinary, 0},
+ {"filter", eCmdHdlrString, 0},
+ {"tag", eCmdHdlrString, 0},
+ {"ruleset", eCmdHdlrString, 0},
+ {"no_buffer", eCmdHdlrBinary, 0},
+ {"buffer_size", eCmdHdlrPositiveInt, 0},
+ {"buffer_timeout", eCmdHdlrPositiveInt, 0},
+ {"packet_count", eCmdHdlrPositiveInt, 0}
+};
+static struct cnfparamblk inppblk = {
+ CNFPARAMBLK_VERSION,
+ sizeof(inppdescr) / sizeof(struct cnfparamdescr),
+ inppdescr
+};
+
+/* module-global parameters */
+static struct cnfparamdescr modpdescr[] = {
+ {"snap_length", eCmdHdlrPositiveInt, 0},
+ {"metadata_only", eCmdHdlrBinary, 0},
+ {"metadata_container", eCmdHdlrGetWord, 0},
+ {"data_container", eCmdHdlrGetWord, 0}
+};
+static struct cnfparamblk modpblk = {
+ CNFPARAMBLK_VERSION,
+ sizeof(modpdescr) / sizeof(struct cnfparamdescr),
+ modpdescr
+};
+
+#include "im-helper.h"
+
+/*
+ * create input instance, set default parameters, and
+ * add it to the list of instances.
+ */
+static rsRetVal
+createInstance(instanceConf_t **pinst) {
+ instanceConf_t *inst;
+ DEFiRet;
+ CHKmalloc(inst = malloc(sizeof(instanceConf_t)));
+ inst->next = NULL;
+ inst->interface = NULL;
+ inst->filePath = NULL;
+ inst->device = NULL;
+ inst->promiscuous = 0;
+ inst->filter = NULL;
+ inst->tag = NULL;
+ inst->pszBindRuleset = NULL;
+ inst->immediateMode = 0;
+ inst->bufTimeout = 10;
+ inst->bufSize = 1024 * 1024 * 15; /* should be enough for up to 10Gb interface*/
+ inst->pktBatchCnt = 5;
+
+ /* node created, let's add to global config */
+ if (loadModConf->tail == NULL) {
+ loadModConf->tail = loadModConf->root = inst;
+ } else {
+ loadModConf->tail->next = inst;
+ loadModConf->tail = inst;
+ }
+
+ *pinst = inst;
+finalize_it:
+ RETiRet;
+}
+
+/* input instances */
+
+BEGINnewInpInst
+struct cnfparamvals *pvals;
+instanceConf_t *inst;
+int i;
+CODESTARTnewInpInst
+ pvals = nvlstGetParams(lst, &inppblk, NULL);
+
+ if(pvals == NULL) {
+ LogError(0, RS_RET_MISSING_CNFPARAMS,
+ "impcap: required parameters are missing\n");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ CHKiRet(createInstance(&inst));
+
+ for (i = 0 ; i<inppblk.nParams ; ++i) {
+ if (!pvals[i].bUsed)
+ continue;
+ if (!strcmp(inppblk.descr[i].name, "interface")) {
+ inst->interface = (char *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(inppblk.descr[i].name, "file")) {
+ inst->filePath = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(inppblk.descr[i].name, "promiscuous")) {
+ inst->promiscuous = (uint8_t)pvals[i].val.d.n;
+ }
+ else if (!strcmp(inppblk.descr[i].name, "filter")) {
+ inst->
+ filter = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(inppblk.descr[i].name, "tag")) {
+ inst->tag = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(inppblk.descr[i].name, "ruleset")) {
+ inst->pszBindRuleset = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(inppblk.descr[i].name, "no_buffer")) {
+ inst->immediateMode = (uint8_t)pvals[i].val.d.n;
+ }
+ else if (!strcmp(inppblk.descr[i].name, "buffer_size")) {
+ inst->bufSize = (uint32_t)pvals[i].val.d.n;
+ }
+ else if (!strcmp(inppblk.descr[i].name, "buffer_timeout")) {
+ inst->bufTimeout = (uint8_t)pvals[i].val.d.n;
+ }
+ else if (!strcmp(inppblk.descr[i].name, "packet_count")) {
+ inst->pktBatchCnt = (uint8_t)pvals[i].val.d.n;
+ }
+ else {
+ dbgprintf("impcap: non-handled param %s in beginCnfLoad\n", inppblk.descr[i].name);
+ }
+ }
+
+finalize_it:
+
+CODE_STD_FINALIZERnewInpInst
+ cnfparamvalsDestruct(pvals, &inppblk);
+ENDnewInpInst
+
+/* global mod conf (v2 system) */
+BEGINsetModCnf
+ struct cnfparamvals *pvals = NULL;
+ int i;
+
+CODESTARTsetModCnf
+ pvals = nvlstGetParams(lst, &modpblk, NULL);
+ if (pvals == NULL) {
+ LogError(0, RS_RET_MISSING_CNFPARAMS, "impcap: error processing module "
+ "config parameters missing [module(...)]");
+ ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
+ }
+
+ for (i = 0 ; i<modpblk.nParams ; ++i) {
+ if (!pvals[i].bUsed)
+ continue;
+ if (!strcmp(modpblk.descr[i].name, "snap_length")) {
+ loadModConf->snap_length = (int)pvals[i].val.d.n;
+ }
+ else if (!strcmp(modpblk.descr[i].name, "metadata_only")) {
+ loadModConf->metadataOnly = (uint8_t)pvals[i].val.d.n;
+ }
+ else if (!strcmp(modpblk.descr[i].name, "metadata_container")) {
+ loadModConf->metadataContainer = (char *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else if (!strcmp(modpblk.descr[i].name, "data_container")) {
+ loadModConf->dataContainer = (char *)es_str2cstr(pvals[i].val.d.estr, NULL);
+ }
+ else {
+ dbgprintf("impcap: non-handled param %s in beginSetModCnf\n", modpblk.descr[i].name);
+ }
+ }
+
+ if (!loadModConf->metadataContainer)
+ CHKmalloc(loadModConf->metadataContainer = strdup(DEFAULT_META_CONTAINER));
+
+ if (!loadModConf->dataContainer)
+ CHKmalloc(loadModConf->dataContainer = strdup(DEFAULT_DATA_CONTAINER));
+finalize_it:
+ if (pvals != NULL)
+ cnfparamvalsDestruct(pvals, &modpblk);
+ENDsetModCnf
+
+/* config v2 system */
+
+BEGINbeginCnfLoad
+CODESTARTbeginCnfLoad
+ loadModConf = pModConf;
+ loadModConf->pConf = pConf;
+ loadModConf->metadataOnly = 0;
+ loadModConf->snap_length = 65535;
+ loadModConf->metadataContainer = NULL;
+ loadModConf->dataContainer = NULL;
+ENDbeginCnfLoad
+
+BEGINendCnfLoad
+CODESTARTendCnfLoad
+ENDendCnfLoad
+
+
+/* function to generate error message if framework does not find requested ruleset */
+static inline void
+std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst) {
+ LogError(0, NO_ERRCODE, "impcap: ruleset '%s' for interface %s not found - "
+ "using default ruleset instead", inst->pszBindRuleset,
+ inst->interface);
+}
+
+BEGINcheckCnf
+ instanceConf_t *inst;
+CODESTARTcheckCnf
+ if (pModConf->root == NULL) {
+ LogError(0, RS_RET_NO_LISTNERS , "impcap: module loaded, but "
+ "no interface defined - no input will be gathered");
+ iRet = RS_RET_NO_LISTNERS;
+ }
+
+ if (pModConf->metadataOnly) { /* if metadata_only is "on", snap_length is overwritten */
+ pModConf->snap_length = 100; /* arbitrary value, but should be enough for most protocols */
+ }
+
+ if (!pModConf->metadataContainer || !pModConf->dataContainer) {
+ LogError(0, RS_RET_LOAD_ERROR, "impcap: no name defined for metadata_container and "
+ "data_container, this shouldn't happen");
+ }
+ else {
+ DBGPRINTF("impcap: metadata will be stored in '%s', and data in '%s'\n",
+ pModConf->metadataContainer, pModConf->dataContainer);
+ }
+
+ for (inst = pModConf->root ; inst != NULL ; inst = inst->next) {
+ std_checkRuleset(pModConf, inst);
+ if (inst->interface ==NULL &&inst->filePath == NULL) {
+ iRet = RS_RET_INVALID_PARAMS;
+ LogError(0, RS_RET_LOAD_ERROR, "impcap: 'interface' or 'file' must be specified");
+ break;
+ }
+ if (inst->interface !=NULL &&inst->filePath != NULL) {
+ iRet = RS_RET_INVALID_PARAMS;
+ LogError(0, RS_RET_LOAD_ERROR, "impcap: either 'interface' or 'file' must be specified");
+ break;
+ }
+ }
+
+ENDcheckCnf
+
+BEGINactivateCnfPrePrivDrop
+CODESTARTactivateCnfPrePrivDrop
+ runModConf = pModConf;
+ENDactivateCnfPrePrivDrop
+
+BEGINactivateCnf
+ instanceConf_t *inst;
+ pcap_t *dev = NULL;
+ struct bpf_program filter_program;
+ bpf_u_int32 SubNet, NetMask;
+ char errBuf[PCAP_ERRBUF_SIZE];
+ uint8_t retCode = 0;
+CODESTARTactivateCnf
+ for (inst = pModConf->root ; inst != NULL ; inst = inst->next) {
+ if (inst->filePath != NULL) {
+ dev = pcap_open_offline((const char *)inst->filePath, errBuf);
+ if (dev == NULL) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while opening capture file: '%s'", errBuf);
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ }
+ else if (inst->interface != NULL) {
+ dev = pcap_create((const char *)inst->interface, errBuf);
+ if (dev == NULL) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while creating packet capture: '%s'",
+ errBuf);
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+
+ DBGPRINTF("setting snap_length %d\n", pModConf->snap_length);
+ if (pcap_set_snaplen(dev, pModConf->snap_length)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting snap length: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+
+ DBGPRINTF("setting promiscuous %d\n", inst->promiscuous);
+ if (pcap_set_promisc(dev, inst->promiscuous)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting promiscuous mode: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+
+ if (inst->immediateMode) {
+ DBGPRINTF("setting immediate mode %d\n", inst->immediateMode);
+ retCode = pcap_set_immediate_mode(dev, inst->immediateMode);
+ if (retCode) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting immediate mode: '%s',"
+ " using buffer instead\n",pcap_geterr(dev));
+ }
+ }
+
+ if (!inst->immediateMode || retCode){
+ DBGPRINTF("setting buffer size %u \n", inst->bufSize);
+ if (pcap_set_buffer_size(dev, inst->bufSize)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting buffer size: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ DBGPRINTF("setting buffer timeout %dms\n", inst->bufTimeout);
+ if (pcap_set_timeout(dev, inst->bufTimeout)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting buffer timeout: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ }
+
+ switch (pcap_activate(dev)) {
+ case PCAP_WARNING_PROMISC_NOTSUP:
+ LogError(0, NO_ERRCODE, "interface doesn't support promiscuous mode");
+ break;
+ case PCAP_WARNING_TSTAMP_TYPE_NOTSUP:
+ LogError(0, NO_ERRCODE, "timestamp type is not supported");
+ break;
+ case PCAP_WARNING:
+ LogError(0, NO_ERRCODE, "pcap: %s", pcap_geterr(dev));
+ break;
+ case PCAP_ERROR_ACTIVATED:
+ LogError(0, RS_RET_LOAD_ERROR, "already activated, shouldn't happen");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR_NO_SUCH_DEVICE:
+ LogError(0, RS_RET_LOAD_ERROR, "device doesn't exist");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR_PERM_DENIED:
+ LogError(0, RS_RET_LOAD_ERROR, "elevated privilege needed to open capture "
+ "interface");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR_PROMISC_PERM_DENIED:
+ LogError(0, RS_RET_LOAD_ERROR, "elevated privilege needed to put interface "
+ "in promiscuous mode");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR_RFMON_NOTSUP:
+ LogError(0, RS_RET_LOAD_ERROR, "interface doesn't support monitor mode");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR_IFACE_NOT_UP:
+ LogError(0, RS_RET_LOAD_ERROR, "interface is not up");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ case PCAP_ERROR:
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: %s", pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+
+ if (inst->filter != NULL) {
+ DBGPRINTF("getting netmask on %s\n", inst->interface);
+ //obtain the subnet
+ if (pcap_lookupnet(inst->interface, &SubNet, &NetMask, errBuf)){
+ DBGPRINTF("could not get netmask\n");
+ NetMask = PCAP_NETMASK_UNKNOWN;
+ }
+ DBGPRINTF("setting filter to '%s'\n", inst->filter);
+ /* Compile the filter */
+ if (pcap_compile(dev, &filter_program, (const char *)inst->filter, 1, NetMask)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while compiling filter: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ else if (pcap_setfilter(dev, &filter_program)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting filter: '%s'",
+ pcap_geterr(dev));
+ pcap_freecode(& filter_program);
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ pcap_freecode(&filter_program);
+ }
+
+ if (pcap_set_datalink(dev, DLT_EN10MB)) {
+ LogError(0, RS_RET_LOAD_ERROR, "pcap: error while setting datalink type: '%s'",
+ pcap_geterr(dev));
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+ } /* inst->interface != NULL */
+ else {
+ LogError(0, RS_RET_LOAD_ERROR, "impcap: no capture method specified, "
+ "please specify either 'interface' or 'file' in config");
+ ABORT_FINALIZE(RS_RET_LOAD_ERROR);
+ }
+
+ inst->device = dev;
+ }
+
+finalize_it:
+ if(iRet != 0) {
+ if(dev) pcap_close(dev);
+ }
+ENDactivateCnf
+
+BEGINfreeCnf
+ instanceConf_t *inst, *del;
+CODESTARTfreeCnf
+ DBGPRINTF("impcap: freeing confs...\n");
+ for (inst = pModConf->root ; inst != NULL ; ) {
+ del = inst;
+ inst = inst->next;
+ free(del->filePath);
+ free(del->filter);
+ free(del->pszBindRuleset);
+ free(del->interface);
+ free(del->tag);
+ free(del);
+ }
+ free(pModConf->metadataContainer);
+ free(pModConf->dataContainer);
+ DBGPRINTF("impcap: finished freeing confs\n");
+ENDfreeCnf
+
+/* runtime functions */
+
+/*
+ * Converts a list of bytes to their hexadecimal representation in ASCII
+ *
+ * Gets the list of bytes and the length as parameters
+ *
+ * Returns a pointer on the new list, being a string of ASCII characters
+ * representing hexadecimal values, in the form "A5B34C65..."
+ * its size is twice length parameter + 1
+*/
+char *stringToHex(char *string, size_t length) {
+ const char *hexChar = "0123456789ABCDEF";
+ char *retBuf;
+ uint16_t i;
+
+ retBuf = malloc((2 * length + 1) * sizeof(char));
+ for (i = 0; i < length; ++i) {
+ retBuf[2 * i] = hexChar[(string[i] & 0xF0) >> 4];
+ retBuf[2 * i + 1] = hexChar[string[i] & 0x0F];
+ }
+ retBuf[2 * length] = '\0';
+
+ return retBuf;
+}
+
+/*
+ * This method parses every packet received by libpcap, and is called by it
+ * It creates the message for Rsyslog, calls the parsers and add all necessary information
+ * in the message
+*/
+void packet_parse(uchar *arg, const struct pcap_pkthdr *pkthdr, const uchar *packet) {
+ DBGPRINTF("impcap : entered packet_parse\n");
+ smsg_t *pMsg;
+
+ /* Prevent cast error from char to int with arg */
+ union {
+ uchar *buf;
+ int *id;
+ } aux;
+
+ aux.buf = arg;
+ int *id = aux.id;
+ msgConstruct(&pMsg);
+
+ MsgSetInputName(pMsg, pInputName);
+ //search inst in loadmodconf,and check if there is tag. if so set tag in msg.
+ pthread_t ctid = pthread_self();
+ instanceConf_t * inst;
+ for (inst = runModConf->root; inst != NULL; inst = inst->next) {
+ if (pthread_equal(ctid, inst->tid)) {
+ if (inst->pBindRuleset != NULL) {
+ MsgSetRuleset(pMsg, inst->pBindRuleset);
+ }
+ if (inst->tag != NULL) {
+ MsgSetTAG(pMsg, inst->tag, strlen((const char *)inst->tag));
+ }
+ }
+ }
+
+
+ struct json_object *jown = json_object_new_object();
+ json_object_object_add(jown, "ID", json_object_new_int(++(*id)));
+
+ struct syslogTime sysTimePkt;
+ char timeStr[30];
+ struct timeval tv = pkthdr->ts;
+ datetime.timeval2syslogTime(&tv, &sysTimePkt, 1/*inUTC*/);
+ if (datetime.formatTimestamp3339(&sysTimePkt, timeStr)) {
+ json_object_object_add(jown, "timestamp", json_object_new_string(timeStr));
+ }
+
+ json_object_object_add(jown, "net_bytes_total", json_object_new_int(pkthdr->len));
+
+ data_ret_t * dataLeft = eth_parse(packet, pkthdr->caplen, jown);
+
+ json_object_object_add(jown, "net_bytes_data", json_object_new_int(dataLeft->size));
+ char *dataHex = stringToHex(dataLeft->pData, dataLeft->size);
+ if (dataHex != NULL) {
+ struct json_object *jadd = json_object_new_object();
+ json_object_object_add(jadd, "length", json_object_new_int(strlen(dataHex)));
+ json_object_object_add(jadd, "content", json_object_new_string(dataHex));
+ msgAddJSON(pMsg, (uchar *)runModConf->dataContainer, jadd, 0, 0);
+ free(dataHex);
+ }
+ free(dataLeft);
+
+ msgAddJSON(pMsg, (uchar *)runModConf->metadataContainer, jown, 0, 0);
+ submitMsg2(pMsg);
+}
+
+/* This is used to terminate the plugin.
+ */
+static void
+doSIGTTIN(int __attribute__((unused)) sig)
+{
+ pthread_t tid = pthread_self();
+ const int bTerminate = ATOMIC_FETCH_32BIT(&bTerminateInputs, &mutTerminateInputs);
+ DBGPRINTF("impcap: awoken via SIGTTIN; bTerminateInputs: %d\n", bTerminate);
+ if(bTerminate) {
+ for(instanceConf_t *inst = runModConf->root; inst != NULL; inst = inst->next) {
+ if(pthread_equal(tid, inst->tid)) {
+ pcap_breakloop(inst->device);
+ DBGPRINTF("impcap: thread %lx, termination requested via SIGTTIN - telling libpcap\n",
+ (long unsigned int)tid);
+ }
+ }
+ }
+}
+
+/*
+ * This is the main function for each thread
+ * taking care of a specified network interface
+*/
+static ATTR_NORETURN void *startCaptureThread(void *instanceConf) {
+ int id = 0;
+ pthread_t tid = pthread_self();
+
+ /* we want to support non-cancel input termination. To do so, we must signal libpcap
+ * when to stop. As we run on the same thread, we need to register as SIGTTIN handler,
+ * which will be used to put the terminating condition into libpcap.
+ */
+ DBGPRINTF("impcap: setting catch for SIGTTIN, thread %lx\n",
+ (long unsigned int)tid);
+ sigset_t sigSet;
+ struct sigaction sigAct;
+ sigfillset(&sigSet);
+ pthread_sigmask(SIG_BLOCK, &sigSet, NULL);
+ sigemptyset(&sigSet);
+ sigaddset(&sigSet, SIGTTIN);
+ pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL);
+ memset(&sigAct, 0, sizeof (sigAct));
+ sigemptyset(&sigAct.sa_mask);
+ sigAct.sa_handler = doSIGTTIN;
+ sigaction(SIGTTIN, &sigAct, NULL);
+
+ instanceConf_t * inst = (instanceConf_t * )instanceConf;
+ DBGPRINTF("impcap: thread %lx, begin capture!\n",
+ (long unsigned int)tid);
+ while (glbl.GetGlobalInputTermState() == 0) {
+ pcap_dispatch(inst->device, inst->pktBatchCnt, packet_parse, (uchar * ) & id);
+ }
+ DBGPRINTF("impcap: thread %lx, capture finished\n",
+ (long unsigned int)tid);
+ pthread_exit(0);
+}
+
+BEGINrunInput
+ instanceConf_t *inst;
+ int ret = 0;
+CODESTARTrunInput
+ for (inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ /* creates a thread and starts capturing on the interface */
+ ret = pthread_create(&inst->tid, NULL, startCaptureThread, inst);
+ if (ret) {
+ LogError(0, RS_RET_NO_RUN, "impcap: error while creating threads\n");
+ }
+ }
+
+ DBGPRINTF("impcap: starting to wait for close condition\n");
+ // TODO: Use thread for capture instead of just waiting
+ while(glbl.GetGlobalInputTermState() == 0) {
+ if(glbl.GetGlobalInputTermState() == 0)
+ srSleep(0, 400000);
+ }
+
+ DBGPRINTF("impcap: received close signal, signaling instance threads...\n");
+ for (inst = runModConf->root; inst != NULL; inst = inst->next) {
+ pthread_kill(inst->tid, SIGTTIN);
+ }
+
+ DBGPRINTF("impcap: threads signaled, waiting for join...");
+ for (inst = runModConf->root ; inst != NULL ; inst = inst->next) {
+ pthread_join(inst->tid, NULL);
+ pcap_close(inst->device);
+ }
+
+ DBGPRINTF("impcap: finished threads, stopping\n");
+ENDrunInput
+
+BEGINwillRun
+CODESTARTwillRun
+/* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInputName));
+ CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("impcap"), sizeof("impcap") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+finalize_it:
+ENDwillRun
+
+BEGINafterRun
+CODESTARTafterRun
+ if (pInputName != NULL) {
+ prop.Destruct(&pInputName);
+ }
+ENDafterRun
+
+BEGINmodExit
+CODESTARTmodExit
+ DBGPRINTF("impcap:: modExit\n");
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+/* declaration of functions */
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+ CODEqueryEtryPt_STD_IMOD_QUERIES
+ CODEqueryEtryPt_STD_CONF2_QUERIES
+ CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES
+ CODEqueryEtryPt_STD_CONF2_IMOD_QUERIES
+ CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES /* might need it */
+ CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ENDmodInit