From 1f648d6b38f4f3e61481773f6f27ef7a014b687e Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 2 Jun 2024 13:19:18 -0700 Subject: [PATCH] Bug #863: implement tcpr_pcap_open() universally Add macros that can properly convert between PCAP timestamps and timespec/timeval. We still use pcap_open_offline() for tcprewrite.c, otherwise tests fail because timestamps get written inconsistently across libpcap versions. Also fix multiplier calculation error. --- configure.ac | 5 +++++ docs/CHANGELOG | 1 + src/common/fakepcapnav.c | 3 ++- src/common/tcpdump.c | 2 +- src/common/utils.c | 11 +++++++++++ src/common/utils.h | 1 + src/defines.h.in | 22 ++++++++++++++++------ src/replay.c | 10 ---------- src/send_packets.c | 8 ++++---- src/tcpprep.c | 2 +- 10 files changed, 42 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index 70df1540..ce474445 100644 --- a/configure.ac +++ b/configure.ac @@ -1216,6 +1216,11 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ if test $have_pcap_open_offline_with_tstamp_precision = yes ; then AC_DEFINE([HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION], [1], [Does libpcap have pcap_open_offline_with_tstamp_precision?]) + AC_DEFINE([PCAP_TSTAMP_US_TO_NS_MULTIPLIER], [1], [Multiplier for conversion from PCAP usec to nsec]) + AC_DEFINE([PCAP_TSTAMP_US_TO_US_DIVISOR], [1000], [Divisor for conversion from PCAP usec to usec]) +else + AC_DEFINE([PCAP_TSTAMP_US_TO_NS_MULTIPLIER], [1000], [Multiplier for conversion from PCAP usec to nsec]) + AC_DEFINE([PCAP_TSTAMP_US_TO_US_DIVISOR], [1], [Divisor for conversion from PCAP usec to usec]) fi diff --git a/docs/CHANGELOG b/docs/CHANGELOG index a05c2db2..ceea729e 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/01/2024 Version 4.5.0-beta1 + - fix nansecond timestamp regression bug (#863) - --fixhdrlen option added to control action on packet length changes (#846) - incorrect checksum for certain IPv4 packets - fixed by #846 (#844) - add check for IPv6 extension header length (#827 #842) diff --git a/src/common/fakepcapnav.c b/src/common/fakepcapnav.c index 057c9710..ae9a29ec 100644 --- a/src/common/fakepcapnav.c +++ b/src/common/fakepcapnav.c @@ -26,6 +26,7 @@ #include "defines.h" #include "config.h" #include "common.h" +#include "utils.h" #include #ifndef HAVE_PCAPNAV @@ -52,7 +53,7 @@ pcapnav_open_offline(const char *filename) err(-1, "malloc() error: unable to malloc pcapnav_t"); } - pcapnav->pcap = pcap_open_offline(filename, errbuf); + pcapnav->pcap = tcpr_pcap_open(filename, errbuf); if (pcapnav->pcap == NULL) { errx(-1, "Error opening pcap file %s: %s", filename, errbuf); } diff --git a/src/common/tcpdump.c b/src/common/tcpdump.c index 637af839..398024cd 100644 --- a/src/common/tcpdump.c +++ b/src/common/tcpdump.c @@ -75,7 +75,7 @@ tcpdump_print(tcpdump_t *tcpdump, struct pcap_pkthdr *pkthdr, const u_char *data /* convert header to file-format packet header */ actual_pkthdr.ts.ts_sec = (uint32_t)pkthdr->ts.tv_sec; - actual_pkthdr.ts.ts_usec = (uint32_t)pkthdr->ts.tv_sec; + actual_pkthdr.ts.ts_usec = (uint32_t)(pkthdr->ts.tv_usec / PCAP_TSTAMP_US_TO_US_DIVISOR); actual_pkthdr.caplen = pkthdr->caplen; actual_pkthdr.len = pkthdr->len; diff --git a/src/common/utils.c b/src/common/utils.c index 520d40d2..fa6b5738 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -97,6 +97,17 @@ our_safe_strdup(const char *str, const char *funcname, int line, const char *fil return newstr; } +pcap_t* +tcpr_pcap_open(const char *path, char *ebuf) +{ +#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION + return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf); +#else + return pcap_open_offline(path, ebuf); +#endif +} + + /** * calls free and sets to NULL. */ diff --git a/src/common/utils.h b/src/common/utils.h index a858099a..4239ead9 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -47,6 +47,7 @@ void packet_stats(const tcpreplay_stats_t *stats); int format_date_time(struct timespec *when, char *buf, size_t len); uint32_t tcpr_random(uint32_t *seed); void restore_stdin(void); +pcap_t* tcpr_pcap_open(const char *path, char *ebuf); /* our "safe" implimentations of functions which allocate memory */ #define safe_malloc(x) our_safe_malloc(x, __FUNCTION__, __LINE__, __FILE__) diff --git a/src/defines.h.in b/src/defines.h.in index 1a26132c..86214943 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -359,14 +359,24 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t typedef u_int32_t uint32_t (a)->tv_nsec = (b)->tv_nsec; \ } while (0) -/* libpcap puts nanosec values in tv_usec when pcap file is read with nanosec precision*/ -#define TIMEVAL_AS_TIMESPEC_SET(a, b) \ - do { \ - (a)->tv_sec = (b)->tv_sec; \ - (a)->tv_nsec = (b)->tv_usec * 1000; \ +/* libpcap that supports it, puts nanosecond values in tv_usec when pcap file is read with nanosec precision, + * and so tv_usec is directly copied to tv_nsec. + * But older versions do that do not support nanosecond precision need to multiply tv_usec by 1000 to convert + * to tv_nsec. + */ +#define PCAP_TIMEVAL_TO_TIMESPEC_SET(a, b) \ + do { \ + (b)->tv_sec = (a)->tv_sec; \ + (b)->tv_nsec = (a)->tv_usec * PCAP_TSTAMP_US_TO_NS_MULTIPLIER; \ + } while(0) + +#define PCAP_TIMEVAL_TO_TIMEVAL_SET(a, b) \ + do { \ + (b)->tv_sec = (a)->tv_sec; \ + (b)->tv_usec = (a)->tv_usec / PCAP_TSTAMP_US_TO_US_DIVISOR; \ } while(0) -/* +/* * Help suppress some compiler warnings * No problem if variable is actually used */ diff --git a/src/replay.c b/src/replay.c index 74b0ab68..59f84303 100644 --- a/src/replay.c +++ b/src/replay.c @@ -98,16 +98,6 @@ tcpr_replay_index(tcpreplay_t *ctx) return rcode; } -static pcap_t * -tcpr_pcap_open(const char *path, char *ebuf) -{ -#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION - return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf); -#else - return pcap_open_offline(path, ebuf); -#endif -} - /** * \brief replay a pcap file out interface(s) * diff --git a/src/send_packets.c b/src/send_packets.c index 788341b4..fdb2e37b 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -299,7 +299,7 @@ preload_pcap_file(tcpreplay_t *ctx, int idx) if (close(1) == -1) warnx("unable to close stdin: %s", strerror(errno)); - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) + if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) errx(-1, "Error opening pcap file: %s", ebuf); dlt = pcap_datalink(pcap); @@ -388,7 +388,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) while (!ctx->abort && read_next_packet && (pktdata = get_next_packet(options, pcap, &pkthdr, idx, prev_packet)) != NULL) { struct timespec pkthdr_ts; - TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec + PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr.ts, &pkthdr_ts); now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -739,9 +739,9 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * if (options->speed.mode == speed_multiplier) { struct timespec pkthdr_ts; - TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &pkthdr_ts); + PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr_ptr->ts, &pkthdr_ts); if (!timesisset(&last_pkt_ts)) { - TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &last_pkt_ts); + PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr_ptr->ts, &last_pkt_ts); } else if (timescmp(&pkthdr_ts, &last_pkt_ts, >)) { struct timespec delta; diff --git a/src/tcpprep.c b/src/tcpprep.c index 52fdc8aa..83824766 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -91,7 +91,7 @@ main(int argc, char *argv[]) readpcap: /* open the pcap file */ - if ((options->pcap = pcap_open_offline(OPT_ARG(PCAP), errbuf)) == NULL) { + if ((options->pcap = tcpr_pcap_open(OPT_ARG(PCAP), errbuf)) == NULL) { close(out_file); tcpprep_close(tcpprep); errx(-1, "Error opening libpcap: %s", errbuf);