blob: e4b549d271addb06167ea55916ff168c29ebd723 [file] [log] [blame]
/*
*
* (C) 2005-13 - ntop.org
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lessed General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*
*/
#include "pfring_mod_dag.h"
//#define DAG_DEBUG
int pfring_dag_open(pfring *ring) {
int i = 0;
pfring_dag *d = NULL;
uint32_t mindata;
struct timeval maxwait;
struct timeval poll;
daginf_t* info;
uint8_t stream_erf_types[MAX_CARD_ERF_TYPES];
uint8_t supported = 0;
#ifdef DAG_DEBUG
printf("[PF_RING] DAG open\n");
#endif
ring->close = pfring_dag_close;
ring->stats = pfring_dag_stats;
ring->recv = pfring_dag_recv;
ring->set_poll_watermark = pfring_dag_set_poll_watermark;
ring->set_poll_duration = pfring_dag_set_poll_duration;
ring->poll = pfring_dag_poll;
ring->set_direction = pfring_dag_set_direction;
ring->enable_ring = pfring_dag_enable_ring;
ring->priv_data = malloc(sizeof(pfring_dag));
if(ring->priv_data == NULL)
goto ret_error;
memset(ring->priv_data, 0, sizeof(pfring_dag));
d = ring->priv_data;
if(ring->caplen > MAX_CAPLEN)
ring->caplen = MAX_CAPLEN;
d->device_name = (char *) malloc(DAGNAME_BUFSIZE);
if (d->device_name == NULL) {
goto free_private;
}
if (dag_parse_name(ring->device_name, d->device_name, DAGNAME_BUFSIZE, &d->stream_num) < 0) {
fprintf(stderr,"Error: device name not recognized\n");
goto free_device_name;
}
if (d->stream_num % 2) {
fprintf(stderr,"Error: odd-numbered streams are TX streams\n");
goto free_device_name;
}
if((d->fd = dag_open((char *) d->device_name)) < 0) {
fprintf(stderr, "Error opening %s\n", d->device_name);
goto free_device_name;
}
if (dag_attach_stream(d->fd, d->stream_num, 0, 0) < 0) {
fprintf(stderr, "Error attaching to stream %d: is it already attached to another application?\n", d->stream_num);
goto dag_close;
}
if (dag_get_stream_poll(d->fd, d->stream_num, &mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error getting poll info\n");
goto dag_detach;
}
ring->poll_duration = DEFAULT_POLL_DURATION;
mindata = DEFAULT_MIN_PKT_QUEUED * AVG_PACKET_SIZE; //min_pkt=128, avg=512 -> min_bytes=65536
maxwait.tv_sec = ring->poll_duration / 1000;
maxwait.tv_usec = (ring->poll_duration % 1000) * 1000;
if (dag_set_stream_poll(d->fd, d->stream_num, mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error setting poll info\n");
goto dag_detach;
}
if(dag_start_stream(d->fd, d->stream_num) < 0) {
fprintf(stderr, "Error starting stream\n");
goto dag_detach;
}
d->bottom = NULL;
d->top = NULL;
d->strip_crc = 1;
info = dag_info(d->fd);
if (info->device_code == 0x4200 || info->device_code == 0x4230) //these cards already strip the CRC
d->strip_crc = 0;
memset(stream_erf_types, 0, MAX_CARD_ERF_TYPES);
if (dag_get_stream_erf_types(d->fd, d->stream_num, stream_erf_types, MAX_CARD_ERF_TYPES) < 0) {
fprintf(stderr, "Error getting stream type\n");
goto dag_stop;
}
while (stream_erf_types[i] && i<MAX_CARD_ERF_TYPES)
switch(stream_erf_types[i++] & 0x7f) {
case TYPE_ETH:
case TYPE_COLOR_ETH:
case TYPE_DSM_COLOR_ETH:
case TYPE_COLOR_HASH_ETH:
supported = 1;
break;
default:
break;
}
if (!supported){
fprintf(stderr, "Error: stream type not supported\n");
goto dag_stop;
}
return 0;
dag_stop:
dag_stop_stream(d->fd, d->stream_num);
dag_detach:
dag_detach_stream(d->fd, d->stream_num);
dag_close:
dag_close(ring->fd);
free_device_name:
free(d->device_name);
free_private:
free(ring->priv_data);
ret_error:
return -1;
}
/* **************************************************** */
void pfring_dag_close(pfring *ring) {
pfring_dag *d;
if(ring->priv_data == NULL)
return;
d = (pfring_dag *) ring->priv_data;
dag_stop_stream(d->fd, d->stream_num);
dag_detach_stream(d->fd, d->stream_num);
dag_close(d->fd);
free(d->device_name);
free(ring->priv_data);
}
/* **************************************************** */
int pfring_dag_recv(pfring *ring, u_char** buffer, u_int buffer_len, struct pfring_pkthdr *hdr, u_int8_t wait_for_incoming_packet) {
int caplen = 0;
int skip;
dag_record_t *erf_hdr;
uint16_t rlen;
u_char *payload;
uint8_t *ext_hdr_type;
uint32_t ext_hdr_num;
uint32_t len;
unsigned long long ts;
int retval = 0;
pfring_dag *d;
#ifdef DAG_DEBUG
printf("[PF_RING] DAG recv\n");
#endif
if(ring->priv_data == NULL)
return -1;
d = (pfring_dag *) ring->priv_data;
if(ring->reentrant)
pthread_rwlock_wrlock(&ring->rx_lock);
check_and_poll:
if (ring->break_recv_loop)
goto exit; /* retval = 0 */
if ((d->top - d->bottom) < dag_record_size) {
if ( (d->top = dag_advance_stream(d->fd, d->stream_num, (void * /* but it is void** */) &d->bottom)) == NULL) {
retval = -1;
goto exit;
}
if ( (d->top - d->bottom) < dag_record_size && !wait_for_incoming_packet )
goto exit; /* retval = 0 */
goto check_and_poll;
}
erf_hdr = (dag_record_t *) d->bottom;
rlen = ntohs(erf_hdr->rlen);
if (rlen < dag_record_size) {
fprintf(stderr, "Error: wrong record size\n");
retval = -1;
goto exit;
}
d->bottom += rlen;
skip = 0;
switch((erf_hdr->type & 0x7f)) {
case TYPE_PAD:
skip = 1;
case TYPE_ETH:
/* stats update */
if (erf_hdr->lctr) {
if (d->stats_drop > (UINT_MAX - ntohs(erf_hdr->lctr)))
d->stats_drop = UINT_MAX;
else
d->stats_drop += ntohs(erf_hdr->lctr);
}
break;
/* Note:
* In TYPE_COLOR_HASH_ETH, TYPE_DSM_COLOR_ETH, TYPE_COLOR_ETH
* the color value overwrites the lctr */
default:
break;
}
if (skip)
goto check_and_poll;
payload = (u_char *) erf_hdr;
payload += dag_record_size;
/* computing extension headers size */
ext_hdr_type = &erf_hdr->type;
ext_hdr_num = 0;
while ( (*ext_hdr_type & 0x80) && (rlen > (16 + ext_hdr_num * 8)) ) {
ext_hdr_type += 8;
ext_hdr_num++;
}
payload += 8 * ext_hdr_num;
switch((erf_hdr->type & 0x7f)) {
case TYPE_COLOR_HASH_ETH:
case TYPE_DSM_COLOR_ETH:
case TYPE_COLOR_ETH:
/*
* Note:
* In TYPE_COLOR_HASH_ETH, TYPE_DSM_COLOR_ETH, TYPE_COLOR_ETH
* the color value overwrites the lctr
*/
hdr->extended_hdr.pkt_hash /* 32bit */ = erf_hdr->lctr /* 16bit */;
case TYPE_ETH:
len = ntohs(erf_hdr->wlen);
if (d->strip_crc)
len -= 4;
caplen = rlen;
caplen -= dag_record_size;
caplen -= (8 * ext_hdr_num);
caplen -= 2;
if (caplen > ring->caplen)
caplen = ring->caplen;
if (caplen > len)
caplen = len;
if((buffer_len > 0) && (caplen > buffer_len))
caplen = buffer_len;
payload += 2;
break;
default:
#ifdef DAG_DEBUG
printf("Warning: unhandled ERF type\n");
#endif
goto check_and_poll;
}
if (buffer_len > 0){
if(*buffer != NULL && caplen > 0)
memcpy(*buffer, payload, caplen);
}
else
*buffer = payload;
hdr->caplen = caplen;
hdr->len = len;
/* computing timestamp as from DAG docs */
ts = erf_hdr->ts;
hdr->ts.tv_sec = ts >> 32;
ts = (ts & 0xffffffffULL) * 1000000;
ts += 0x80000000;
hdr->ts.tv_usec = ts >> 32;
if (hdr->ts.tv_usec >= 1000000) {
hdr->ts.tv_usec -= 1000000;
hdr->ts.tv_sec++;
}
/* compute pf_ring timestamp_ns from ERF time stamp */
ts = erf_hdr->ts;
ts = (ts & 0xffffffffULL) * 1000000000;
ts += 0x80000000;
ts >>= 32;
ts += ((erf_hdr->ts >> 32) * 1000000000);
hdr->extended_hdr.timestamp_ns = ts;
#ifdef PFRING_DAG_PARSE_PKT
pfring_parse_pkt(*buffer, hdr, 4, 0, 1);
#else
hdr->extended_hdr.parsed_header_len = 0;
#endif
d->stats_recv++;
retval = 1;
exit:
if(ring->reentrant)
pthread_rwlock_unlock(&ring->rx_lock);
return retval;
}
/* **************************************************** */
int pfring_dag_set_poll_watermark(pfring *ring, u_int16_t watermark) {
uint32_t mindata;
struct timeval maxwait;
struct timeval poll;
pfring_dag *d;
if(ring->priv_data == NULL)
return -1;
d = (pfring_dag *) ring->priv_data;
if (dag_get_stream_poll(d->fd, d->stream_num, &mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error getting poll info\n");
return -1;
}
mindata = watermark * AVG_PACKET_SIZE;
if (dag_set_stream_poll(d->fd, d->stream_num, mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error setting poll watermark\n");
return -1;
}
return 0;
}
/* **************************************************** */
int pfring_dag_set_poll_duration(pfring *ring, u_int duration) {
uint32_t mindata;
struct timeval maxwait;
struct timeval poll;
pfring_dag *d;
if(ring->priv_data == NULL)
return -1;
d = (pfring_dag *) ring->priv_data;
if (dag_get_stream_poll(d->fd, d->stream_num, &mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error getting poll info\n");
return -1;
}
ring->poll_duration = duration;
maxwait.tv_sec = ring->poll_duration / 1000;
maxwait.tv_usec = (ring->poll_duration % 1000) * 1000;
if (dag_set_stream_poll(d->fd, d->stream_num, mindata, &maxwait, &poll) < 0) {
fprintf(stderr, "Error setting poll duration\n");
return -1;
}
return 0;
}
/* **************************************************** */
int pfring_dag_stats(pfring *ring, pfring_stat *stats) {
pfring_dag *d;
if(ring->priv_data == NULL)
return -1;
d = (pfring_dag *) ring->priv_data;
stats->recv = d->stats_recv;
stats->drop = d->stats_drop;
return 0;
}
/* **************************************************** */
int pfring_dag_set_direction(pfring *ring, packet_direction direction) {
if (direction == rx_only_direction || direction == rx_and_tx_direction)
return 0;
return -1;
}
/* **************************************************** */
int pfring_dag_enable_ring(pfring *ring) {
/* nothing to do */
return 0;
}
/* **************************************************** */
int pfring_dag_poll(pfring *ring, u_int wait_duration) {
pfring_dag *d;
if(ring->priv_data == NULL)
return -1;
d = (pfring_dag *) ring->priv_data;
if ((d->top - d->bottom) >= dag_record_size)
return 1;
if ( (d->top = dag_advance_stream(d->fd, d->stream_num, (void * /* but it is void** */) &d->bottom)) == NULL)
return -1;
if ( (d->top - d->bottom) < dag_record_size )
return 0;
return 1;
}