blob: 7affabb792e4277537c7f36204a7e650751941a7 [file] [log] [blame]
#include <pcap.h>
#include <signal.h>
#include <sched.h>
#include <stdlib.h>
#define HAVE_PCAP
#include "config.h"
#ifdef HAVE_REDIS
#include <hiredis/hiredis.h>
#endif
#include "pfring.h"
//#include "pfutils.c"
#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 <time.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <net/ethernet.h> /* the L2 protocols */
unsigned long long numPkts = 0, numBytes = 0;
struct gtpv1_header {
u_int8_t flags, message_type;
u_int16_t total_length;
u_int32_t tunnel_id;
u_int16_t sequence_number;
u_int8_t pdu_nuber, next_ext_header;
} __attribute__((__packed__));
#define DEFAULT_DEVICE "eth0"
pfring *pd;
pcap_dumper_t *dumper = NULL;
FILE *dumper_fd = NULL;
int verbose = 0;
u_int32_t num_pkts=0;
/* ******************************** */
void sigproc(int sig) {
static int called = 0;
if(called) return; else called = 1;
if(dumper)
pcap_dump_close(dumper);
else if(dumper_fd)
fclose(dumper_fd);
pfring_close(pd);
printf("\nSaved %d packets on disk\n", num_pkts);
exit(0);
}
/* *************************************** */
void printHelp(void) {
printf("pwrite - (C) 2003-13 Deri Luca <deri@ntop.org>\n");
printf("-h [Print help]\n");
printf("-i <device> [Device name]\n");
printf("-w <dump file> [Dump file path]\n");
printf("-f <BPF filter> [Ingress BPF filter]\n");
#ifdef HAVE_REDIS
printf("-m <imsi> [Dump only the specified IMSI traffic (Example: -m 284031122831060)]\n");
#endif
printf("-g <GTP TEID> [Dump only the specified tunnel (example -g 94148 [dec] or -g 381CE8C0 [hex])]\n");
printf("-d [Save packet digest instead of pcap packets]\n");
printf("-S [Do not strip hw timestamps (if present)]\n");
printf("\n"
"Please consider using n2disk for dumping\n"
"traces at high speed (http://www.ntop.org/products/n2disk/)\n");
}
/* *************************************** */
int32_t gmt2local(time_t t) {
int dt, dir;
struct tm *gmt, *loc;
struct tm sgmt;
if (t == 0)
t = time(NULL);
gmt = &sgmt;
*gmt = *gmtime(&t);
loc = localtime(&t);
dt = (loc->tm_hour - gmt->tm_hour) * 60 * 60 +
(loc->tm_min - gmt->tm_min) * 60;
/*
* If the year or julian day is different, we span 00:00 GMT
* and must add or subtract a day. Check the year first to
* avoid problems when the julian day wraps.
*/
dir = loc->tm_year - gmt->tm_year;
if (dir == 0)
dir = loc->tm_yday - gmt->tm_yday;
dt += dir * 24 * 60 * 60;
return (dt);
}
/* ****************************************************** */
/*
* A faster replacement for inet_ntoa().
*/
char* _intoa(unsigned int addr, char* buf, u_short bufLen) {
char *cp, *retStr;
u_int byte;
int n;
cp = &buf[bufLen];
*--cp = '\0';
n = 4;
do {
byte = addr & 0xff;
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0) {
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0)
*--cp = byte + '0';
}
*--cp = '.';
addr >>= 8;
} while (--n > 0);
/* Convert the string to lowercase */
retStr = (char*)(cp+1);
return(retStr);
}
/* ************************************ */
char* intoa(unsigned int addr) {
static char buf[sizeof "ff:ff:ff:ff:ff:ff:255.255.255.255"];
return(_intoa(addr, buf, sizeof(buf)));
}
/* ************************************ */
inline char* in6toa(struct in6_addr addr6) {
static char buf[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];
snprintf(buf, sizeof(buf),
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
addr6.s6_addr[0], addr6.s6_addr[1], addr6.s6_addr[2],
addr6.s6_addr[3], addr6.s6_addr[4], addr6.s6_addr[5], addr6.s6_addr[6],
addr6.s6_addr[7], addr6.s6_addr[8], addr6.s6_addr[9], addr6.s6_addr[10],
addr6.s6_addr[11], addr6.s6_addr[12], addr6.s6_addr[13], addr6.s6_addr[14],
addr6.s6_addr[15]);
return(buf);
}
/* *************************************** */
#define MAX_NUM_GTP_TUNNELS 8
int main(int argc, char* argv[]) {
char *device = NULL, c, *out_dump = NULL;
u_int flags = 0, dont_strip_hw_ts = 0, dump_digest = 0;
int32_t thiszone;
u_char *p;
char *bpfFilter = NULL;
struct pfring_pkthdr hdr;
u_int num_gtp_tunnels = 0;
u_int32_t gtp_tunnels[MAX_NUM_GTP_TUNNELS];
char *imsi = NULL;
while((c = getopt(argc,argv,"hi:w:Sdg:f:"
#ifdef HAVE_REDIS
"m:"
#endif
)) != -1) {
switch(c) {
case 'd':
dump_digest = 1;
break;
case 'h':
printHelp();
return(0);
break;
case 'w':
out_dump = strdup(optarg);
break;
case 'm':
imsi = strdup(optarg);
#ifdef HAVE_REDIS
{
redisContext *redis = redisConnect("127.0.0.1", 6379);
redisReply* r;
if(redis == NULL) {
printf("WARNING: Unable to connect to local redis 127.0.0.1:6379\n");
// return(-1);
} else {
if(((r = redisCommand(redis, "GET imsi.%s", optarg)) == NULL) || (r->str == NULL)) {
printf("WARNING: Unable to retrieve redis key imsi.%s\n", optarg);
// return(-1);
} else {
if(strtok(r->str, ";") != NULL) {
char *tunnel;
int i;
for(i=0; i<2; i++)
if((tunnel = strtok(NULL, ";")) != NULL) {
gtp_tunnels[num_gtp_tunnels] = atoi(tunnel);
printf("Added GTP tunnel to filter %u/%08X\n",
gtp_tunnels[num_gtp_tunnels], gtp_tunnels[num_gtp_tunnels]);
num_gtp_tunnels++;
}
free(r->str);
}
}
redisFree(redis);
}
}
#endif
break;
case 'g':
if(num_gtp_tunnels < MAX_NUM_GTP_TUNNELS) {
u_int32_t v;
if(strlen(optarg) == 8)
sscanf(optarg, "%08X", &v);
else
v = atoi(optarg);
gtp_tunnels[num_gtp_tunnels++]=v;
printf("Added GTP tunnel to filter %u/%08X\n", v, v);
} else
printf("Too many GTP tunnels defined (-g): ignored\n");
break;
case 'i':
device = strdup(optarg);
break;
case 'S':
dont_strip_hw_ts = 1;
break;
case 'f':
bpfFilter = optarg;
break;
}
}
if(out_dump == NULL) {
printHelp();
return(-1);
}
if(dump_digest) {
if((dumper_fd = fopen(out_dump, "w")) == NULL) {
printf("Unable to create dump file %s\n", out_dump);
return(-1);
}
} else {
dumper = pcap_dump_open(pcap_open_dead(DLT_EN10MB, 16384 /* MTU */), out_dump);
if(dumper == NULL) {
printf("Unable to create dump file %s\n", out_dump);
return(-1);
}
}
memset(&hdr, 0, sizeof(hdr));
flags = PF_RING_PROMISC;
if(dump_digest) flags |= PF_RING_LONG_HEADER;
if(!dont_strip_hw_ts) flags |= PF_RING_STRIP_HW_TIMESTAMP;
if((pd = pfring_open(device, 1520, flags)) == NULL) {
printf("pfring_open error [%s]\n", strerror(errno));
return(-1);
} else
pfring_set_application_name(pd, "pwrite");
thiszone = gmt2local(0);
printf("Capture device: %s\n", device);
printf("Dump file path: %s\n", out_dump);
if(bpfFilter != NULL) {
int rc = pfring_set_bpf_filter(pd, bpfFilter);
if(rc != 0)
printf("pfring_set_bpf_filter(%s) returned %d\n", bpfFilter, rc);
else
printf("Successfully set BPF filter '%s'\n", bpfFilter);
}
signal(SIGINT, sigproc);
pfring_enable_ring(pd);
if(dumper_fd) fprintf(dumper_fd, "# Time\tLen\tEth Type\tVLAN\tL3 Proto\tSrc IP:Port\tDst IP:Port\n");
while(1) {
if(pfring_recv(pd, &p, 0, &hdr, 1 /* wait_for_packet */) > 0) {
if(dumper) {
if((num_gtp_tunnels > 0) || (imsi != NULL)) {
memset(&hdr.extended_hdr, 0, sizeof(hdr.extended_hdr));
pfring_parse_pkt((u_char*)p, (struct pfring_pkthdr*)&hdr, 5, 0, 0);
#ifdef DEBUG
if(hdr.extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4*/ ) {
printf("[IPv4][%s:%d ", intoa(hdr.extended_hdr.parsed_pkt.ipv4_src), hdr.extended_hdr.parsed_pkt.l4_src_port);
printf("-> %s:%d] ", intoa(hdr.extended_hdr.parsed_pkt.ipv4_dst), hdr.extended_hdr.parsed_pkt.l4_dst_port);
} else {
printf("[IPv6][%s:%d ", in6toa(hdr.extended_hdr.parsed_pkt.ipv6_src), hdr.extended_hdr.parsed_pkt.l4_src_port);
printf("-> %s:%d] ", in6toa(hdr.extended_hdr.parsed_pkt.ipv6_dst), hdr.extended_hdr.parsed_pkt.l4_dst_port);
}
printf("[TEID: %08X]\n", hdr.extended_hdr.parsed_pkt.tunnel.tunnel_id);
#endif
if((hdr.extended_hdr.parsed_pkt.tunnel.tunnel_id != 0xFFFFFFFF)
&& (hdr.extended_hdr.parsed_pkt.l3_proto == IPPROTO_UDP)
&& (((hdr.extended_hdr.parsed_pkt.l4_src_port == 2123) && (hdr.extended_hdr.parsed_pkt.l4_dst_port == 2123))
|| ((hdr.extended_hdr.parsed_pkt.l4_src_port == 2152) && (hdr.extended_hdr.parsed_pkt.l4_dst_port == 2152)))) {
u_int8_t found = 0, i;
struct gtpv1_header *g = (struct gtpv1_header*)&p[hdr.extended_hdr.parsed_pkt.offset.payload_offset];
if((g->message_type == 0x10) /* Create Request */
|| (g->message_type == 0x12) /* Update Request */) {
u_int16_t displ = 12+hdr.extended_hdr.parsed_pkt.offset.payload_offset;
while(displ < hdr.caplen) {
u_int8_t field_id = p[displ];
if(field_id == 0x02 /* IMSI */) {
int i, j = 0;
char *_imsi = (char*)&p[displ+1], u_imsi[24];
for(i = 0; i < 8; i++) {
if((_imsi[i] & 0x0F) <= 9)
u_imsi[j++] = (_imsi[i] & 0x0F) + 0x30;
if(((_imsi[i] >> 4) & 0x0F) <= 9)
u_imsi[j++] = ((_imsi[i] >> 4) & 0x0F) + 0x30;
}
u_imsi[j] = '\0';
if(strcmp(imsi, u_imsi) == 0) {
; /* Ok we can dump the packet */
} else
continue;
displ += 9;
break;
} else {
switch(field_id) {
case 0x03: /* Routing Area Info */
displ += 7;
break;
case 0x14: /* NSAPI */
displ += 2;
break;
case 0x00: /* Ignore */
case 0x01: /* Cause */
case 0x08: /* Reordering Required */
case 0x0E: /* Recovery */
case 0x0F: /* Selection Mode */
case 0x13: /* Teardown Indicator */
case 0xB4: /* PS Handover XID Parameters 7.7.79 */
displ += 2;
break;
case 0x10: /* TEID Data */
displ += 5;
break;
case 0x11: /* TEID Control */
displ += 5;
break;
case 0x1A: /* Charging Characteristics */
displ += 3;
break;
case 0x7F: /* Charging ID */
displ += 5;
break;
default:
displ += ntohs(*(u_int16_t*)&p[displ+1]) + 3;
break;
}
}
} /* while */
} else if(g->message_type == 0xFF /* Data */) {
#ifdef DEBUG
printf("%08X\n", hdr.extended_hdr.parsed_pkt.tunnel.tunnel_id);
#endif
for(i=0; i<num_gtp_tunnels; i++)
if(gtp_tunnels[i] == hdr.extended_hdr.parsed_pkt.tunnel.tunnel_id) {
found = 1;
break;
}
if(!found) continue;
} else
continue;
} else
continue;
}
#ifdef DEBUG
printf("Dump \n");
#endif
pcap_dump((u_char*)dumper, (struct pcap_pkthdr*)&hdr, p);
} else {
u_int32_t s, usec, nsec;
if(hdr.ts.tv_sec == 0) {
memset((void*)&hdr.extended_hdr.parsed_pkt, 0, sizeof(struct pkt_parsing_info));
pfring_parse_pkt((u_char*)p, (struct pfring_pkthdr*)&hdr, 5, 1, 1);
}
s = (hdr.ts.tv_sec + thiszone) % 86400;
if(hdr.extended_hdr.timestamp_ns) {
if (pd->dna.dna_dev.mem_info.device_model != intel_igb_82580 /* other than intel_igb_82580 */)
s = ((hdr.extended_hdr.timestamp_ns / 1000000000) + thiszone) % 86400;
/* "else" intel_igb_82580 has 40 bit ts, using gettimeofday seconds:
* be careful with drifts mixing sys time and hw timestamp */
usec = (hdr.extended_hdr.timestamp_ns / 1000) % 1000000;
nsec = hdr.extended_hdr.timestamp_ns % 1000;
} else {
usec = hdr.ts.tv_usec, nsec = 0;
}
fprintf(dumper_fd, "%02d:%02d:%02d.%06u%03u"
"\t%d\t%04X\t%u\t%d",
s / 3600, (s % 3600) / 60, s % 60, usec, nsec,
hdr.len,
hdr.extended_hdr.parsed_pkt.eth_type,
hdr.extended_hdr.parsed_pkt.vlan_id,
hdr.extended_hdr.parsed_pkt.l3_proto);
if(hdr.extended_hdr.parsed_pkt.eth_type == 0x0800 /* IPv4*/ ) {
fprintf(dumper_fd, "\t%s:%d\t", intoa(hdr.extended_hdr.parsed_pkt.ipv4_src), hdr.extended_hdr.parsed_pkt.l4_src_port);
fprintf(dumper_fd, "\t%s:%d\n", intoa(hdr.extended_hdr.parsed_pkt.ipv4_dst), hdr.extended_hdr.parsed_pkt.l4_dst_port);
} else if(hdr.extended_hdr.parsed_pkt.eth_type == 0x86DD /* IPv6*/) {
fprintf(dumper_fd, "\t%s:%d", in6toa(hdr.extended_hdr.parsed_pkt.ipv6_src), hdr.extended_hdr.parsed_pkt.l4_src_port);
fprintf(dumper_fd, "\t%s:%d\n", in6toa(hdr.extended_hdr.parsed_pkt.ipv6_dst), hdr.extended_hdr.parsed_pkt.l4_dst_port);
} else
fprintf(dumper_fd, "\n");
}
num_pkts++;
}
}
if(dumper)
pcap_dump_close(dumper);
else if(dumper_fd)
fclose(dumper_fd);
pfring_close(pd);
return(0);
}