Skip to content

Commit 0ee84f2

Browse files
committed
Add time-regulated pcap playback with infinite looping
1 parent 4b51fce commit 0ee84f2

File tree

4 files changed

+149
-49
lines changed

4 files changed

+149
-49
lines changed

config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
/* config.h.in. Generated from configure.ac by autoheader. */
22

3+
/* Define to 1 if you have the <byteswap.h> header file. */
4+
#undef HAVE_BYTESWAP_H
5+
36
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
47
#undef HAVE_DOPRNT
58

configure.ac

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Process this file with autoconf to produce a configure script.
33

44
AC_PREREQ([2.71])
5-
AC_INIT([sflowtool],[6.06])
5+
AC_INIT([sflowtool],[6.07])
66
AC_CONFIG_SRCDIR([src/sflowtool.c])
77
AM_INIT_AUTOMAKE
88
AC_PROG_CC
@@ -18,7 +18,7 @@ case "$host" in
1818
esac
1919

2020
# Checks for header files.
21-
AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h])
21+
AC_CHECK_HEADERS([fcntl.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h byteswap.h])
2222

2323
# Checks for typedefs, structures, and compiler characteristics.
2424
AC_C_CONST

sflowtool.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Summary: tool to ascii-print or forward sFlow datagrams
22
Name: sflowtool
3-
Version: 6.06
3+
Version: 6.07
44
Release: 1%{?dist}
55
License: https://www.inmon.com/technology/sflowlicense.txt
66
Group: Productivity/Networking/Diagnostic

src/sflowtool.c

Lines changed: 143 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,22 @@ extern "C" {
2929
#include <inttypes.h>
3030
#include <arpa/inet.h>
3131
#include <netdb.h>
32+
33+
#if HAVE_BYTESWAP_H
3234
#include <byteswap.h>
35+
#else
36+
#define bswap_16(value) \
37+
((((value) & 0xff) << 8) | ((value) >> 8))
38+
39+
#define bswap_32(value) \
40+
(((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \
41+
(uint32_t)bswap_16((uint16_t)((value) >> 16)))
42+
43+
#define bswap_64(value) \
44+
(((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) << 32) | \
45+
(uint64_t)bswap_32((uint32_t)((value) >> 32)))
46+
#endif
47+
3348
#include <getopt.h>
3449

3550
#include "sflow.h" /* sFlow v5 */
@@ -202,11 +217,14 @@ typedef struct _SFConfig {
202217
char *readPcapFileName;
203218
FILE *readPcapFile;
204219
struct pcap_file_header readPcapHdr;
220+
struct pcap_pkthdr pcapPktHdr;
205221
int pcapSwap;
222+
uint64_t pcapStart_mS;
223+
uint64_t pcapNext_mS;
224+
uint64_t pcapStartOffset_mS;
206225
/* sFlow-from-pcap generator */
207226
uint32_t pcapSamplingN;
208227
SFDDgram *sFlowDatagram;
209-
uint64_t pcap_uS;
210228
double playback;
211229
uint32_t output_sample_pool;
212230
uint32_t output_sample_seqNo;
@@ -5971,38 +5989,55 @@ static int pcapOffsetToSFlow(uint8_t *start, int len)
59715989
}
59725990

59735991
/*_________________---------------------------__________________
5974-
_________________ readPcapPacket __________________
5992+
_________________ readPcapPacketHeader __________________
59755993
-----------------___________________________------------------
59765994
*/
59775995

5978-
static int readPcapPacket(FILE *file)
5996+
static int readPcapPacketHdr(FILE *file, struct pcap_pkthdr *hdr, uint64_t *pcap_uS)
59795997
{
5980-
uint8_t buf[SA_MAX_PCAP_PKT];
5981-
struct pcap_pkthdr hdr;
5982-
SFSample sample;
5983-
int skipBytes = 0;
5984-
5985-
if(fread(&hdr, sizeof(hdr), 1, file) != 1) {
5998+
if(fread(hdr, sizeof(*hdr), 1, file) != 1) {
59865999
if(feof(file)) return 0;
59876000
fprintf(ERROUT, "unable to read pcap packet header from %s : %s\n", sfConfig.readPcapFileName, strerror(errno));
59886001
exit(-32);
59896002
}
59906003

59916004
if(sfConfig.pcapSwap) {
5992-
hdr.ts_sec = bswap_32(hdr.ts_sec);
5993-
hdr.ts_usec = bswap_32(hdr.ts_usec);
5994-
hdr.caplen = bswap_32(hdr.caplen);
5995-
hdr.len = bswap_32(hdr.len);
6005+
hdr->ts_sec = bswap_32(hdr->ts_sec);
6006+
hdr->ts_usec = bswap_32(hdr->ts_usec);
6007+
hdr->caplen = bswap_32(hdr->caplen);
6008+
hdr->len = bswap_32(hdr->len);
59966009
}
59976010

59986011
/* Protect against possible buffer overrun from corrupted pcap file.
59996012
Thanks to Naoki Ogawa for spotting this. */
6000-
if(hdr.caplen > SA_MAX_PCAP_PKT) {
6001-
fprintf(ERROUT, "pcap %s : capture-len (%u) too long for read buffer\n", sfConfig.readPcapFileName, hdr.caplen);
6013+
if(hdr->caplen > SA_MAX_PCAP_PKT) {
6014+
fprintf(ERROUT, "pcap %s : capture-len (%u) too long for read buffer\n", sfConfig.readPcapFileName, hdr->caplen);
60026015
return 0;
60036016
}
60046017

6005-
if(fread(buf, hdr.caplen, 1, file) != 1) {
6018+
if(pcap_uS) {
6019+
// tell caller where we are (in pcap uS time)
6020+
uint64_t t_uS = hdr->ts_sec;
6021+
t_uS *= 1000000;
6022+
t_uS += hdr->ts_usec;
6023+
*pcap_uS = t_uS;
6024+
}
6025+
6026+
return 1;
6027+
}
6028+
6029+
/*_________________---------------------------__________________
6030+
_________________ readPcapPacket __________________
6031+
-----------------___________________________------------------
6032+
*/
6033+
6034+
static int readPcapPacket(FILE *file, struct pcap_pkthdr *hdr)
6035+
{
6036+
uint8_t buf[SA_MAX_PCAP_PKT];
6037+
SFSample sample;
6038+
int skipBytes = 0;
6039+
6040+
if(fread(buf, hdr->caplen, 1, file) != 1) {
60066041
fprintf(ERROUT, "unable to read pcap packet from %s : %s\n", sfConfig.readPcapFileName, strerror(errno));
60076042
exit(-34);
60086043
}
@@ -6026,9 +6061,9 @@ static int readPcapPacket(FILE *file)
60266061
sfd_xdr_enc_int32(pktsmp, 1); // number of elements
60276062
sfd_xdr_start_tlv(pktsmp, SFLFLOW_HEADER);
60286063
sfd_xdr_enc_int32(pktsmp, SFLHEADER_ETHERNET_ISO8023); // header protocol - TODO: learn from pcap
6029-
sfd_xdr_enc_int32(pktsmp, hdr.len + 4); // frame_length
6064+
sfd_xdr_enc_int32(pktsmp, hdr->len + 4); // frame_length
60306065
sfd_xdr_enc_int32(pktsmp, 4); // stripped (FCS)
6031-
uint32_t caplen = hdr.caplen;
6066+
uint32_t caplen = hdr->caplen;
60326067
if(caplen > SFL_DEFAULT_HEADER_SIZE)
60336068
caplen = SFL_DEFAULT_HEADER_SIZE;
60346069
sfd_xdr_enc_int32(pktsmp, caplen); // header len
@@ -6038,40 +6073,61 @@ static int readPcapPacket(FILE *file)
60386073
SFDAddSample(sfConfig.sFlowDatagram, pktsmp);
60396074
}
60406075

6041-
if(sfConfig.playback < 100) {
6042-
uint64_t pcap_uS = (hdr.ts_sec * 1000000) + hdr.ts_usec;
6043-
if(sfConfig.pcap_uS) {
6044-
// apply playback-speed time compression factor
6045-
uint64_t wait_uS = (pcap_uS - sfConfig.pcap_uS) / sfConfig.playback;
6046-
if((now_mS(NULL) - SFDLastSend_mS(sfConfig.sFlowDatagram)) > 500
6047-
|| wait_uS > 500000)
6048-
SFDSend(sfConfig.sFlowDatagram); // flush before sleep
6049-
usleep(wait_uS);
6050-
}
6051-
sfConfig.pcap_uS = pcap_uS;
6052-
}
6076+
/* TODO: check this logic - should perhaps be happening in select loop? */
6077+
if((now_mS(NULL) - SFDLastSend_mS(sfConfig.sFlowDatagram)) > 500)
6078+
SFDSend(sfConfig.sFlowDatagram);
60536079
}
60546080
else {
60556081
// reading full sFlow datagrams from pcap file
6056-
if(hdr.caplen < hdr.len) {
6082+
if(hdr->caplen < hdr->len) {
60576083
fprintf(ERROUT, "incomplete datagram (pcap snaplen too short)\n");
60586084
}
60596085
else {
60606086
/* need to skip over the encapsulation in the captured packet.
60616087
-- should really do this by checking for 802.2, IP options etc. but
60626088
for now we just assume ethernet + IP + UDP */
6063-
skipBytes = pcapOffsetToSFlow(buf, hdr.caplen);
6089+
skipBytes = pcapOffsetToSFlow(buf, hdr->caplen);
60646090
memset(&sample, 0, sizeof(sample));
60656091
sample.rawSample = buf + skipBytes;
6066-
sample.rawSampleLen = hdr.caplen - skipBytes;
6067-
sample.pcapTimestamp = hdr.ts_sec;
6092+
sample.rawSampleLen = hdr->caplen - skipBytes;
6093+
sample.pcapTimestamp = hdr->ts_sec;
60686094
receiveSFlowDatagram(&sample);
60696095
}
60706096
}
60716097
return 1;
60726098
}
60736099

60746100

6101+
/*_________________---------------------------__________________
6102+
_________________ initPcapPlayback __________________
6103+
-----------------___________________________------------------
6104+
*/
6105+
6106+
static int initPcapPlayback(void) {
6107+
/* tee up the time-regulated infinite playback */
6108+
uint64_t pcap_uS = 0;
6109+
int found = readPcapPacketHdr(sfConfig.readPcapFile, &sfConfig.pcapPktHdr, &pcap_uS);
6110+
if(found) {
6111+
sfConfig.pcapStart_mS = pcap_uS / 1000;
6112+
sfConfig.pcapStartOffset_mS = now_mS(NULL) - sfConfig.pcapStart_mS;
6113+
sfConfig.pcapNext_mS = sfConfig.pcapStart_mS;
6114+
}
6115+
return found;
6116+
}
6117+
6118+
/*_________________---------------------------__________________
6119+
_________________ pcapPlaybackNextSend_mS __________________
6120+
-----------------___________________________------------------
6121+
*/
6122+
6123+
static uint64_t pcapPlaybackNextSend_mS(void) {
6124+
double pcap_rel_mS = (double)(sfConfig.pcapNext_mS - sfConfig.pcapStart_mS);
6125+
double pcap_rel_scaled_mS = pcap_rel_mS / sfConfig.playback;
6126+
return sfConfig.pcapStartOffset_mS
6127+
+ sfConfig.pcapStart_mS
6128+
+ (uint64_t) pcap_rel_scaled_mS;
6129+
}
6130+
60756131
/*_________________---------------------------__________________
60766132
_________________ parseVlanFilter __________________
60776133
-----------------___________________________------------------
@@ -6504,7 +6560,9 @@ static void process_command_line(int argc, char *argv[])
65046560
break;
65056561
case 'P':
65066562
sfConfig.playback = atof(optarg);
6507-
if(sfConfig.playback <= 000001)
6563+
if(sfConfig.playback <= 0)
6564+
sfConfig.playback = 0;
6565+
else if(sfConfig.playback <= 0.00001)
65086566
sfConfig.playback = 0.00001;
65096567
break;
65106568
case 'x': sfConfig.removeContent = YES; break;
@@ -6659,21 +6717,26 @@ int main(int argc, char *argv[])
66596717
if(sfConfig.outputFormat == SFLFMT_PCAP
66606718
|| sfConfig.outputFormat == SFLFMT_PCAP_DISCARD)
66616719
writePcapHeader();
6662-
if(sfConfig.readPcapFile) {
6663-
/* just use a blocking read */
6664-
#ifdef SFL_PCAP_LOOP
6720+
6721+
if(sfConfig.readPcapFile
6722+
&& sfConfig.playback == 0) {
6723+
/* just read flat out until done */
66656724
for(;;) {
6666-
uint32_t pcount;
6667-
while(readPcapPacket(sfConfig.readPcapFile))
6668-
pcount++;
6669-
fprintf(ERROUT, "%"PRIu64":%u\n", now_mS(NULL), pcount);
6670-
fseek(sfConfig.readPcapFile, sizeof(struct pcap_file_header), SEEK_SET);
6725+
if(!readPcapPacketHdr(sfConfig.readPcapFile, &sfConfig.pcapPktHdr, NULL))
6726+
break;
6727+
if(!readPcapPacket(sfConfig.readPcapFile, &sfConfig.pcapPktHdr))
6728+
break;
66716729
}
6672-
#else
6673-
while(readPcapPacket(sfConfig.readPcapFile));
6674-
#endif
66756730
}
66766731
else {
6732+
/* use select loop */
6733+
6734+
if(sfConfig.readPcapFile
6735+
&& sfConfig.playback != 0) {
6736+
/* tee up the time-regulated infinite playback */
6737+
initPcapPlayback();
6738+
}
6739+
66776740
fd_set readfds;
66786741
/* set the select mask */
66796742
FD_ZERO(&readfds);
@@ -6715,6 +6778,40 @@ int main(int argc, char *argv[])
67156778
if(soc4 != -1 && FD_ISSET(soc4, &readfds)) readPacket(soc4);
67166779
if(soc6 != -1 && FD_ISSET(soc6, &readfds)) readPacket(soc6);
67176780
}
6781+
6782+
if(sfConfig.pcapStartOffset_mS) {
6783+
/* in monotonic time, when should the next packet go out? */
6784+
uint64_t send_mS = pcapPlaybackNextSend_mS();
6785+
uint64_t now = now_mS(NULL);
6786+
int atEOF = 0;
6787+
// printf("now=%lu, send_mS=%lu\n", now, send_mS);
6788+
/* keep sending pcap packets until we catch up to monotonic "now" */
6789+
while(now > send_mS) {
6790+
uint64_t pcap_uS = 0;
6791+
if(readPcapPacket(sfConfig.readPcapFile, &sfConfig.pcapPktHdr) == 0
6792+
|| readPcapPacketHdr(sfConfig.readPcapFile, &sfConfig.pcapPktHdr, &pcap_uS) == 0) {
6793+
atEOF = 1;
6794+
break;
6795+
}
6796+
else {
6797+
uint64_t pcap_mS = pcap_uS / 1000;
6798+
/* insist that pcapNext_mS must never go backwards */
6799+
if(pcap_mS >= sfConfig.pcapNext_mS)
6800+
sfConfig.pcapNext_mS = pcap_mS;
6801+
send_mS = pcapPlaybackNextSend_mS();
6802+
// printf("now=%lu, pcap_mS=%lu pcapNext_mS=%lu send_mS=%lu\n",
6803+
// now, pcap_mS, sfConfig.pcapNext_mS, send_mS);
6804+
}
6805+
}
6806+
if(atEOF) {
6807+
/* seek back to start of file */
6808+
fseek(sfConfig.readPcapFile, sizeof(struct pcap_file_header), SEEK_SET);
6809+
/* and restart the playback */
6810+
initPcapPlayback();
6811+
}
6812+
6813+
}
6814+
67186815
}
67196816
}
67206817
return 0;

0 commit comments

Comments
 (0)