blob: e6e35aa4d88569c5c8392c3de45f8d8445e9aa48 [file] [log] [blame]
/*
*
* (C) 2005-12 - Luca Deri <deri@ntop.org>
* Alfredo Cardigliano <cardigliano@ntop.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* VLAN support courtesy of Vincent Magnin <vincent.magnin@ci.unil.ch>
*
*/
#define _GNU_SOURCE
#include <signal.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/poll.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/ethernet.h> /* the L2 protocols */
#include <sys/time.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include "pfring.h"
#include "pfutils.c"
#define ALARM_SLEEP 1
pfring_stat pfringStats;
char *in_dev = NULL, *out_dev = NULL;
int in_ifindex, out_ifindex;
u_int8_t wait_for_packet = 1, do_shutdown = 0;
static struct timeval startTime;
int mode = 0;
int bidirectional = 0;
int cluster_id = -1;
int flush = 0;
int print_interface_stats = 0;
pfring *pd1, *pd2, *pdb1, *pdb2;
pfring_dna_bouncer *bouncer_handle = NULL, *bouncer_handle2;
u_int numCPU;
int bind_core[2];
struct dir_info {
u_int64_t __padding __attribute__((__aligned__(64)));
u_int64_t numPkts;
u_int64_t numBytes __attribute__((__aligned__(64)));
};
struct dir_info dir_stats[2];
u_int8_t handle_ts_card = 0;
/* ******************************** */
void print_stats() {
struct timeval endTime;
double deltaMillisec;
static u_int8_t print_all;
static u_int64_t lastPkts = 0;
static u_int64_t lastBytes = 0;
double diff, bytesDiff;
static struct timeval lastTime;
char buf1[64], buf2[64], buf3[64];
unsigned long long nBytes = 0, nPkts = 0;
double thpt;
if(startTime.tv_sec == 0) {
gettimeofday(&startTime, NULL);
print_all = 0;
} else
print_all = 1;
gettimeofday(&endTime, NULL);
deltaMillisec = delta_time(&endTime, &startTime);
nBytes = dir_stats[0].numBytes + dir_stats[1].numBytes;
nPkts = dir_stats[0].numPkts + dir_stats[1].numPkts;
{
thpt = ((double)8*nBytes)/(deltaMillisec*1000);
fprintf(stderr, "---\nAbsolute Stats: %s pkts - %s bytes",
pfring_format_numbers((double)nPkts, buf1, sizeof(buf1), 0),
pfring_format_numbers((double)nBytes, buf2, sizeof(buf2), 0));
if(print_all)
fprintf(stderr, " [%s pkt/sec - %s Mbit/sec]\n",
pfring_format_numbers((double)(nPkts*1000)/deltaMillisec, buf1, sizeof(buf1), 1),
pfring_format_numbers(thpt, buf2, sizeof(buf2), 1));
else
fprintf(stderr, "\n");
if (print_interface_stats) {
pfring_stat if_stats;
if (pfring_stats(pd1, &if_stats) >= 0)
fprintf(stderr, " %s RX %" PRIu64 " pkts Dropped %" PRIu64 " pkts (%.1f %%)\n",
mode == 1 ? "Consumer Queue" : pd1->device_name, if_stats.recv, if_stats.drop,
if_stats.recv == 0 ? 0 : ((double)(if_stats.drop*100)/(double)(if_stats.recv + if_stats.drop)));
if (mode == 0 && bidirectional) {
if (pfring_stats(bidirectional == 2 ? pdb1 : pd2, &if_stats) >= 0)
fprintf(stderr, " %s RX %" PRIu64 " pkts Dropped %" PRIu64 " pkts (%.1f %%)\n",
(bidirectional == 2 ? pdb1 : pd2)->device_name, if_stats.recv, if_stats.drop,
if_stats.recv == 0 ? 0 : ((double)(if_stats.drop*100)/(double)(if_stats.recv + if_stats.drop)));
}
}
if(print_all && (lastTime.tv_sec > 0)) {
deltaMillisec = delta_time(&endTime, &lastTime);
diff = nPkts-lastPkts;
bytesDiff = nBytes - lastBytes;
bytesDiff /= (1000*1000*1000)/8;
fprintf(stderr, "Actual Stats: %llu pkts [%s ms][%s pps/%s Gbps]\n",
(long long unsigned int)diff,
pfring_format_numbers(deltaMillisec, buf1, sizeof(buf1), 1),
pfring_format_numbers(((double)diff/(double)(deltaMillisec/1000)), buf2, sizeof(buf2), 1),
pfring_format_numbers(((double)bytesDiff/(double)(deltaMillisec/1000)), buf3, sizeof(buf3), 1)
);
}
lastPkts = nPkts, lastBytes = nBytes;
}
lastTime.tv_sec = endTime.tv_sec, lastTime.tv_usec = endTime.tv_usec;
}
/* ******************************** */
void my_sigalarm(int sig) {
if(do_shutdown) {
exit(0);
}
print_stats();
alarm(ALARM_SLEEP);
signal(SIGALRM, my_sigalarm);
}
/* ******************************** */
void sigproc(int sig) {
static int called = 0;
fprintf(stderr, "Leaving...\n");
if(called) return; else called = 1;
do_shutdown = 1;
switch (mode) {
case 0:
pfring_dna_bouncer_breakloop(bouncer_handle);
if (bidirectional == 2)
pfring_dna_bouncer_breakloop(bouncer_handle2);
break;
case 1:
case 2:
pfring_breakloop(pd1);
break;
}
}
/* *************************************** */
void printHelp(void) {
printf("pfdnabounce - (C) 2011-12 ntop.org\n");
printf("\nForward traffic from -a -> -b device using DNA\n\n");
printf("pfdnabounce [-v] [-a] -i in_dev\n");
printf("-h Print this help\n");
printf("-i <device> Device name (RX)\n");
printf("-o <device> Device name (TX)\n");
printf("-m <mode> Specifies the library support to use\n"
" 0 - DNA Bouncer (default)\n"
" 1 - DNA Cluster (use -c <id>)\n"
" 2 - Standard DNA\n");
printf("-c <id> DNA Cluster id\n");
printf("-b <mode> Bridge mode (forward in both directions):\n"
" 0 - disabled (default)\n"
" 1 - single thread (DNA Cluster and Bouncer only)\n"
" 2 - two threads, one per direction (DNA Bouncer only)\n");
printf("-f Flush packets immediately (do not use watermarks)\n");
printf("-g <core id> Bind this app to a core (with -b 2 use <core id>:<core id>)\n");
printf("-a Active packet wait\n");
printf("-p Print per-interface absolute stats\n");
exit(0);
}
/* *************************************** */
int dummyProcessPacketZero(u_int32_t *pkt_len, u_char *pkt, const u_char *user_bytes, u_int8_t direction) {
struct dir_info *di;
u_int32_t len = *pkt_len;
#ifdef DEBUG
int i = 0;
for(i=0; i<32; i++) printf("%02X ", pkt[i]);
printf("\n");
#endif
if(unlikely(handle_ts_card)) {
u_int8_t ts_len = 0;
switch(pkt[len-1]) {
case 0xC3: ts_len = 9; break;
case 0xC2: ts_len = 5; break;
}
// printf("ts_len: %u\n", ts_len);
len -= ts_len;
}
if (bidirectional == 2)
di = (struct dir_info *) user_bytes;
else
di = &dir_stats[direction];
#if 0 /* change something inside the packet */
{
u_int16_t *nshort2 = (u_int16_t *) (&pkt[16]);
u_int16_t *nshort3 = (u_int16_t *) (&pkt[18]);
u_int16_t newhshort3 = ntohs(*nshort2) + 1;
//printf(" [>] Changing 0x%04X 0x%04X\n", ntohs(*nshort2), ntohs(*nshort3));
*nshort2 = *nshort3;
*nshort3 = htons(newhshort3);
//printf("to 0x%04X 0x%04X\n", ntohs(*nshort2), ntohs(*nshort3));
}
#endif
di->numPkts++;
di->numBytes += len + 24 /* 8 Preamble + 4 CRC + 12 IFG */;
*pkt_len = len;
return DNA_BOUNCER_PASS;
}
/* *************************************** */
void packetConsumerLoopZeroCluster() {
struct pfring_pkthdr h;
int tx_ifindex;
pfring_pkt_buff *pkt_handle = NULL;
memset(&h, 0, sizeof(h));
if ((pkt_handle = pfring_alloc_pkt_buff(pd1)) == NULL) {
printf("Error allocating pkt buff\n");
return;
}
while (!do_shutdown) {
if (pfring_recv_pkt_buff(pd1, pkt_handle, &h, wait_for_packet) > 0) {
if (bidirectional && h.extended_hdr.if_index == in_ifindex)
tx_ifindex = out_ifindex;
else if (bidirectional && h.extended_hdr.if_index == out_ifindex)
tx_ifindex = in_ifindex;
else if (!bidirectional && h.extended_hdr.if_index == in_ifindex)
tx_ifindex = out_ifindex;
else {
/* unexpected packet, skipping */
printf("Unexpected packet from interface %d: skipping\n", h.extended_hdr.if_index);
continue;
}
if (pfring_set_pkt_buff_ifindex(pd1, pkt_handle, tx_ifindex) == PF_RING_ERROR_INVALID_ARGUMENT) {
printf("Wrong interface id: skipping packet\n");
continue;
}
pfring_send_pkt_buff(pd1, pkt_handle, flush);
}
dir_stats[(h.extended_hdr.if_index == out_ifindex)].numPkts++;
dir_stats[(h.extended_hdr.if_index == out_ifindex)].numBytes += h.len + 24 /* 8 Preamble + 4 CRC + 12 IFG */;
}
}
/* *************************************** */
void dummyProcessPacket(const struct pfring_pkthdr *h, const u_char *p, const u_char *user_bytes) {
#ifdef DEBUG
int i = 0;
for(i=0; i<32; i++) printf("%02X ", p[i]);
printf("\n");
#endif
pfring_send(pd2, (char*)p, h->caplen, flush);
dir_stats[0].numPkts++;
dir_stats[0].numBytes += h->len + 24 /* 8 Preamble + 4 CRC + 12 IFG */;
}
/* *************************************** */
void* bouncer_dir2_thread(void *data) {
if (bind_core[1] >= 0)
bind2core(bind_core[1]);
if(pfring_dna_bouncer_loop(bouncer_handle2, dummyProcessPacketZero, (u_char *) &dir_stats[1], wait_for_packet) == -1) {
printf("Problems while starting bouncer. See dmesg for details.\n");
exit(-1);
}
return NULL;
}
/* *************************************** */
int main(int argc, char* argv[]) {
char c;
char buf[32];
char *bind_mask = NULL;
u_int32_t version;
pthread_t pthread2;
bind_core[0] = bind_core[1] = -1;
dir_stats[0].numPkts = dir_stats[1].numPkts = 0;
dir_stats[0].numBytes = dir_stats[1].numBytes = 0;
numCPU = sysconf( _SC_NPROCESSORS_ONLN );
startTime.tv_sec = 0;
while((c = getopt(argc,argv,"hai:o:m:b:c:g:fpt")) != -1) {
switch(c) {
case 'h':
printHelp();
break;
case 'a':
wait_for_packet = 0;
break;
case 'i':
in_dev = strdup(optarg);
break;
case 'o':
out_dev = strdup(optarg);
break;
case 'm':
mode = atoi(optarg);
break;
case 'c':
cluster_id = atoi(optarg);
break;
case 'b':
bidirectional = atoi(optarg);
break;
case 'f':
flush = 1;
break;
case 'g':
bind_mask = strdup(optarg);
break;
case 'p':
print_interface_stats = 1;
break;
case 't':
handle_ts_card = 1;
break;
}
}
if (in_dev == NULL) printHelp();
if (out_dev == NULL) out_dev = strdup(in_dev);
if (mode < 0 || mode > 2) printHelp();
if (bidirectional < 0 || bidirectional > 2) printHelp();
if (bidirectional == 1 && mode != 0 && mode != 1) printHelp();
if (bidirectional == 2 && mode != 0) printHelp();
if (bidirectional && strcmp(in_dev, out_dev) == 0) printHelp();
if (mode == 1 && cluster_id < 0) printHelp();
printf("Bouncing packets from %s to %s (%s)\n", in_dev, out_dev, bidirectional ? "two-way" : "one-way");
if(handle_ts_card) printf("Stripping timestamp before bouncing packets\n");
if(bind_mask != NULL) {
char *id;
if ((id = strtok(bind_mask, ":")) != NULL)
bind_core[0] = atoi(id) % numCPU;
if ((id = strtok(NULL, ":")) != NULL)
bind_core[1] = atoi(id) % numCPU;
}
switch (mode) {
case 0:
if (bidirectional == 2 /* bidirectional with one thread per direction: opening two bouncer, two sockets per bouncer */) {
pdb1 = pfring_open(out_dev, 1500 /* snaplen */, PF_RING_PROMISC);
if(pdb1 == NULL) {
printf("pfring_open %s error [%s]\n", in_dev, strerror(errno));
return(-1);
}
pfring_set_socket_mode(pdb1, recv_only_mode);
pfring_set_application_name(pdb1, "pfdnabounce");
pdb2 = pfring_open(in_dev, 1500 /* snaplen */, PF_RING_PROMISC);
if(pdb2 == NULL) {
printf("pfring_open %s error [%s]\n", out_dev, strerror(errno));
return(-1);
}
pfring_set_socket_mode(pdb2, send_only_mode);
pfring_set_application_name(pdb2, "pfdnabounce");
}
/* no break here! */
case 2:
pd1 = pfring_open(in_dev, 1500 /* snaplen */, PF_RING_PROMISC);
if(pd1 == NULL) {
printf("pfring_open %s error [%s]\n", in_dev, strerror(errno));
return(-1);
}
if (bidirectional != 1)
pfring_set_socket_mode(pd1, recv_only_mode);
pd2 = pfring_open(out_dev, 1500 /* snaplen */, bidirectional ? PF_RING_PROMISC : 0);
if(pd2 == NULL) {
printf("pfring_open %s error [%s]\n", out_dev, strerror(errno));
return(-1);
}
if (bidirectional != 1)
pfring_set_socket_mode(pd2, send_only_mode);
pfring_set_application_name(pd2, "pfdnabounce");
break;
case 1:
snprintf(buf, sizeof(buf), "dnacluster:%d", cluster_id);
pd1 = pfring_open(buf, 1500 /* snaplen */, PF_RING_PROMISC);
if(pd1 == NULL) {
printf("pfring_open %s error [%s] (please run \"pfdnacluster_master -i %s,%s -c %d -s\")\n", buf, strerror(errno), in_dev, out_dev, cluster_id);
return(-1);
}
pfring_set_socket_mode(pd1, send_and_recv_mode);
break;
}
pfring_version(pd1, &version);
printf("Using PF_RING v.%d.%d.%d\n", (version & 0xFFFF0000) >> 16,
(version & 0x0000FF00) >> 8, version & 0x000000FF);
pfring_set_application_name(pd1, "pfdnabounce");
signal(SIGINT, sigproc);
signal(SIGTERM, sigproc);
signal(SIGINT, sigproc);
signal(SIGALRM, my_sigalarm);
alarm(ALARM_SLEEP);
if(bind_core[0] >= 0)
bind2core(bind_core[0]);
switch (mode) {
case 0:
printf("Using Libzero DNA Bouncer (zero-copy)\n");
if ((bouncer_handle = pfring_dna_bouncer_create(pd1, pd2)) == NULL) {
printf("WARNING: Unable to initialize the DNA Bouncer (ports already in use ?)\n");
pfring_close(pd1);
pfring_close(pd2);
return(-1);
}
if (bidirectional == 2) {
if ((bouncer_handle2 = pfring_dna_bouncer_create(pdb1, pdb2)) == NULL) {
printf("WARNING: Unable to initialize the second DNA Bouncer (ports already in use ?)\n");
pfring_dna_bouncer_destroy(bouncer_handle);
pfring_close(pdb1);
pfring_close(pdb2);
return(-1);
}
printf("Starting direction 0 thread..\n");
pthread_create(&pthread2, NULL, bouncer_dir2_thread, NULL);
printf("Starting direction 1 thread..\n");
} else if (bidirectional == 1) {
if (pfring_dna_bouncer_set_mode(bouncer_handle, two_way_mode) < 0) {
printf("Error setting the DNA Bouncer to bidirectional\n");
pfring_dna_bouncer_destroy(bouncer_handle);
return(-1);
}
}
if(pfring_dna_bouncer_loop(bouncer_handle, dummyProcessPacketZero, (u_char *) &dir_stats[0], wait_for_packet) == -1) {
printf("Problems while starting bouncer. See dmesg for details.\n");
}
if (bidirectional == 2) {
pthread_join(pthread2, NULL);
pfring_dna_bouncer_destroy(bouncer_handle2);
}
pfring_dna_bouncer_destroy(bouncer_handle);
break;
case 1:
printf("Using Libzero DNA Cluster (0-copy)\n");
if (pfring_get_device_ifindex(pd1, in_dev, &in_ifindex ) < 0 ||
pfring_get_device_ifindex(pd1, out_dev, &out_ifindex) < 0) {
printf("Error retrieving interface id\n");
pfring_close(pd1);
return(-1);
}
pfring_enable_ring(pd1);
packetConsumerLoopZeroCluster();
pfring_close(pd1);
break;
case 2:
printf("Using Standard DNA (1-copy)\n");
pfring_set_direction(pd1, rx_only_direction);
pfring_set_direction(pd2, tx_only_direction);
pfring_enable_ring(pd1);
pfring_enable_ring(pd2);
pfring_loop(pd1, dummyProcessPacket, (u_char*) NULL, wait_for_packet);
pfring_close(pd1);
pfring_close(pd2);
break;
}
sleep(3);
return(0);
}