You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/contrib/tile_pcie_logd/tile_pcie_logd.c

371 lines
12 KiB
C

Add option on Tile-Gx for logging for fast.log alerts over PCIe When running on a TILEncore-Gx PCIe card, setting the filetype of fast.log to pcie, will open a connection over PCIe to a host application caleld tile-pcie-logd, that receives the alert strings and writes them to a file on the host. The file name to open is also passed over the PCIe link. This allows running Suricata on the TILEncore-Gx PCIe card, but have the alerts logged to the host system's file system efficiently. The PCIe API that is used is the Tilera Packet Queue (PQ) API which can access PCIe from User Space, thus avoiding system calls. Created util-logopenfile-tile.c and util-logopen-tile.h for the TILE specific PCIe logging functionality. Using Write() and Close() function pointers in LogFileCtx, which default to standard write and close for files and sockets, but are changed to PCIe write and close functions when a PCIe channel is openned for logging. Moved Logging contex out of tm-modules.h into util-logopenfile.h, where it makes more sense. This required including util-logopenfile.h into a couple of alert-*.c files, which previously were getting the definitions from tm-modules.h. The source and Makefile for tile-pcie-logd are added in contrib/tile-pcie-logd. By default, the file name for fast.log specified in suricata.yaml is used as the filename on the host. An optional argument to tile-pcie-logd, --prefix=, can be added to prepend the supplied file path. For example, is the file in suricata.yaml is specified as "/var/log/fast.log" and --prefix="/tmp", then the file will be written to "/tmp/var/log/fast.log". Check for TILERA_ROOT environment variable before building tile_pcie_logd Building tile_pcie_logd on x86 requires the Tilera MDE for its PCIe libraries and API header files. Configure now checs for TILERA_ROOT before enabling builing tile_pcie_logd in contrib/tile_pcie_logd
11 years ago
/* Copyright (C) 2013-2014 Tilera Corporation.
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Ken Steele, Tilera Corporation <suricata@tilera.com>
* \author Tom DeCanio <decanio.tom@gmail.com>
*
* Host side of PCIe alert logging from Suricata running on a
* TILEncore-Gx card.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <assert.h>
#include <asm/tilegxpci.h>
#define CHECK_SEQ_NUM 1
/* The "/dev/tilegxpci%d" device to be used. */
unsigned int card_index = 0;
unsigned int debug = 0;
/*
* When set, drop all alerts rather than write to file. This is for
* testing performance on a file system that can't write files fast
* enough.
*/
unsigned int drop_alerts = 0;
/* Packet queue index. */
unsigned int queue_index;
/* Prefix added to all file paths sent from PCIe card. */
char * path_prefix = NULL;
/* By default, the host ring buffer is 4MB in size and it consists of
* 1024 entries of 4KB buffers. Modify GXPCI_HOST_PQ_RING_ENTRIES in
* <asm/tilegxpci.h> to change the size of each buffer. To increase
* the total ring buffer size, re-configure the host kernel using
* CONFIG_FORCE_MAX_ZONEORDER.
*/
#ifdef GXPCI_HOST_PQ_SEGMENT_ENTRIES
/* New definitions for MDE 4.1.5 */
#define RING_BUF_ELEMS GXPCI_HOST_PQ_SEGMENT_ENTRIES
#define RING_BUF_ELEM_SIZE (HOST_PQ_SEGMENT_MAX_SIZE / GXPCI_HOST_PQ_SEGMENT_ENTRIES)
#else
/* Definitions prior to MDE 4.1.5 */
#define RING_BUF_ELEMS GXPCI_HOST_PQ_RING_ENTRIES
#define RING_BUF_ELEM_SIZE (HOST_PQ_RING_BUF_MAX_SIZE / GXPCI_HOST_PQ_RING_ENTRIES)
#endif
/*********************************************************************/
/* Host-Side Packet Consumer */
/*********************************************************************/
#define TAIL_UPDATE_LIMIT_ENABLE
#define OP_OPEN 1
#define OP_WRITE 2
#define OP_CLOSE 3
typedef struct {
uint32_t magic;
uint32_t fileno;
uint32_t op;
uint32_t seq;
volatile uint32_t len;
uint32_t next_offset;
char buf[];
} TrioMsg;
typedef struct {
FILE *fd;
} FDesc;
#define MAX_FDESC 1024
static FDesc *fdesc[MAX_FDESC];
void run_pcie_logging(void)
{
char dev_name[40];
int pq_fd;
unsigned int host_ring_buffer_size = RING_BUF_ELEMS * RING_BUF_ELEM_SIZE;
volatile TrioMsg *p;
printf("Waiting for PCIe logging data from card %d on queue %d...\n",
card_index, queue_index);
if (path_prefix) {
printf("PCIe logging into directory: '%s'\n", path_prefix);
fflush(stdout);
}
/* Open the packet queue file. */
snprintf(dev_name, sizeof(dev_name), "/dev/tilegxpci%d/packet_queue/t2h/%d",
card_index, queue_index);
do {
pq_fd = open(dev_name, O_RDWR);
if (pq_fd < 0) {
sleep(1);
}
} while (pq_fd < 0);
/* mmap the register space. */
struct gxpci_host_pq_regs_app* pq_regs =
(struct gxpci_host_pq_regs_app*)
mmap(0, sizeof(struct gxpci_host_pq_regs_app),
PROT_READ | PROT_WRITE,
MAP_SHARED, pq_fd, TILEPCI_PACKET_QUEUE_INDICES_MMAP_OFFSET);
if (pq_regs == MAP_FAILED) {
fprintf(stderr, "Failed to mmap PCIe control registers.\n");
exit(EXIT_FAILURE);
}
/* Configure and allocate the ring buffer for the receive queue. */
tilepci_packet_queue_info_t buf_info;
buf_info.buf_size = RING_BUF_ELEM_SIZE;
int err = ioctl(pq_fd, TILEPCI_IOC_SET_PACKET_QUEUE_BUF, &buf_info);
if (err < 0) {
fprintf(stderr, "Failed TILEPCI_IOC_SET_PACKET_QUEUE_BUF: %s\n",
strerror(errno));
abort();
}
/* On the host side, mmap the receive queue region. */
void* buffer =
mmap(0, host_ring_buffer_size, PROT_READ | PROT_WRITE,
MAP_SHARED, pq_fd, TILEPCI_PACKET_QUEUE_BUF_MMAP_OFFSET);
assert(buffer != MAP_FAILED);
/* On the host side, mmap the queue status. */
struct tlr_pq_status *pq_status =
mmap(0, sizeof(struct tlr_pq_status), PROT_READ | PROT_WRITE,
MAP_SHARED, pq_fd, TILEPCI_PACKET_QUEUE_STS_MMAP_OFFSET);
assert(pq_status != MAP_FAILED);
pq_regs->consumer_index = 0;
uint64_t packet_count = 0;
volatile uint32_t write;
uint32_t read = 0;
#ifdef CHECK_SEQ_NUM
uint32_t expect_seq = 1;
#endif
#ifdef HOST_INTERRUPT_MODE
volatile uint32_t* producer_index = &(pq_status->drv_consumer_index);
#else
volatile uint32_t* producer_index = &(pq_regs->producer_index);
#endif
volatile uint32_t* consumer_index = &(pq_regs->consumer_index);
volatile enum gxpci_chan_status_t* status = &(pq_status->status);
while (1) {
if (*status == GXPCI_CHAN_RESET) {
printf("Tile to Host PCIe logging channel was reset.\n");
fflush(stdout);
return;
}
// Get packets off the ring buffer by accessing the receive queue at
// the new write index.
write = *producer_index;
while (write != read) {
if (*status == GXPCI_CHAN_RESET) {
printf("Tile to Host PCIe logging channel was reset.\n");
fflush(stdout);
return;
}
packet_count++;
p = (TrioMsg *)(buffer + ((read&(RING_BUF_ELEMS-1))*RING_BUF_ELEM_SIZE));
if (debug) {
fprintf(stdout, "got a message\n");
fprintf(stdout, "p->magic: %d\n", p->magic);
fprintf(stdout, "p->fileno: %d\n", p->fileno);
#ifdef CHECK_SEQ_NUM
fprintf(stdout, "p->seq: %d\n", p->seq);
#endif
fprintf(stdout, "p->len: %d\n", p->len);
fprintf(stdout, "p->next_offset: %d\n", p->next_offset);
fprintf(stdout, "p->buf: ");
fwrite(&p->buf, sizeof(char), p->len - offsetof(TrioMsg, buf), stdout);
fprintf(stdout, "\n");
fflush(stdout);
}
#ifdef CHECK_SEQ_NUM
if (p->seq != expect_seq) {
/* Check for a reset before reporting a bad sequence
* number to prevent confusing users. */
if (*status == GXPCI_CHAN_RESET) {
printf("Tile to Host PCIe logging channel was reset.\n");
fflush(stdout);
return;
}
fprintf(stderr, "BAD sequence expected %d got %d\n", expect_seq, p->seq);
return;
}
expect_seq = p->seq + 1;
#endif
switch (p->op) {
case OP_OPEN:
if (p->fileno < MAX_FDESC) {
fdesc[p->fileno] = malloc(sizeof(FDesc));
if (fdesc[p->fileno]) {
char mode[2];
mode[0] = p->buf[0];
mode[1] = '\0';
char *file_name = (char *)&p->buf[1];
if (path_prefix) {
/* Added path_prefix to the start of the
* file name. Added space for '\0' and '\'.
* By default, no prefix is added. */
int new_size = strlen(path_prefix) + strlen(file_name) + 1 + 1;
char *new_name = malloc(new_size);
if (!new_name) {
fprintf(stderr, "Failed to allocate memory for %s/%s\n",
path_prefix, file_name);
return;
}
snprintf(new_name, new_size, "%s/%s",
path_prefix, file_name);
file_name = new_name;
}
if ((fdesc[p->fileno]->fd = fopen(file_name, mode)) == NULL) {
fprintf(stderr, "Could not open %s: %s\n",
file_name, strerror(errno));
} else {
printf("Opened '%s' for logging.\n", file_name);
fflush(stdout);
}
}
} else {
fprintf(stderr, "File number %d exceeds Max of %d\n", p->fileno, MAX_FDESC);
}
break;
case OP_WRITE:
if (drop_alerts) {
/* TODO: Report alert count periodically. */
} else {
if (fdesc[p->fileno] && fdesc[p->fileno]->fd) {
fwrite(&p->buf, sizeof(char),
p->len - offsetof(TrioMsg, buf),
fdesc[p->fileno]->fd);
fflush(fdesc[p->fileno]->fd);
}
}
break;
case OP_CLOSE:
if (fdesc[p->fileno] && fdesc[p->fileno]->fd) {
fclose( fdesc[p->fileno]->fd);
free(fdesc[p->fileno]);
fdesc[p->fileno] = NULL;
}
break;
}
read++;
/* Update the read index register to inform the tile side
* that the packet has been read. */
#ifdef TAIL_UPDATE_LIMIT_ENABLE
if ((packet_count & 0x3f) == 0)
*consumer_index = read;
#else
*consumer_index = read;
#endif
}
}
return;
}
/*
* Match argument list option.
* Options ending in '=' take an additional value, which may be
* attached in the same argument or detached in the following
* argument.
* @param arglist Points to remaining argv, updated on match.
* @param option The option to match, ending in '=' if it takes a value.
* @return Value if option matches, NULL otherwise.
*/
char *shift_option(char ***arglist, const char* option)
{
char** args = *arglist;
char* arg = args[0], **rest = &args[1];
int optlen = strlen(option);
char* val = arg+optlen;
if (option[optlen - 1] != '=') {
if (strcmp(arg, option))
return NULL;
} else {
if (strncmp(arg, option, optlen-1))
return NULL;
if (arg[optlen- 1 ] == '\0')
val = *rest++;
else if (arg[optlen - 1] != '=')
return NULL;
}
*arglist = rest;
return val;
}
int main(int argc, char** argv)
{
char **args = &argv[1];
/*
* Scan command line options.
*/
while (*args) {
char* opt = NULL;
if ((opt = shift_option(&args, "--queue_index=")))
queue_index = strtoul(opt, NULL, 0);
else if ((opt = shift_option(&args, "--card=")))
card_index = strtoul(opt, NULL, 0);
else if ((opt = shift_option(&args, "--debug")))
debug = 1;
else if ((opt = shift_option(&args, "--drop")))
drop_alerts = 1;
else if ((opt = shift_option(&args, "--prefix=")))
path_prefix = opt;
else {
fprintf(stderr, "Unknown option '%s'.\n", args[0]);
exit(EXIT_FAILURE);
}
}
run_pcie_logging();
return 0;
}