rigtorp / udpreplay

Replay UDP packets from a pcap file
MIT License
262 stars 72 forks source link

Improve time fidelity of replayed stream #17

Closed sobomax closed 4 years ago

sobomax commented 5 years ago

Current timing algorithm that assumes usleep() is precise and that send() and other code takes no time to execute is rather naive. This change replaces it with one based around clock_nanosleep() anchored to CLOCK_MONOTINIC system clock.

I've made some tests and made a little spreadsheet here which illustrates the difference between the current code and new code. Here you can see recording of a 17 second RTP session captured and analyzed with Wireshark. Spreadsheet "original" corresponds to an original capture, "current" to the replay by the master code as of c8564ae30cd193c6e0443df67daeb5c942333dbe and "precise_timings" to the replay with the proposed change. As you can see, as expected there is consistent 6.55% skew in timings produced by the udpreplay.

Technically speaking, constant packet rate code needs similar fix, but for our purposes this part is not critical.

rigtorp commented 5 years ago

I think the approach here is great. If we instead used libpcaps PCAP_TSTAMP_PRECISION_NANO flag all timestamps would be in nanoseconds, simplifying the math.

sobomax commented 5 years ago

I think the approach here is great. If we instead used libpcaps PCAP_TSTAMP_PRECISION_NANO flag all timestamps would be in nanoseconds, simplifying the math.

Yeah, this can be done, however internally libpcap uses tv_usec timestamps for its on-disk format, so it won't do much except saving few lines of code.

rigtorp commented 5 years ago

No it supports storing nanoseconds on disk, I use this feature:

/usr/sbin/tcpdump -i XXX -j adapter --time-stamp-precision=nano
rigtorp commented 5 years ago

If you want to have very accurate replay you probably want to use a busy loop instead of sleeping:

while (std::chrono::system_time::now() < deadline);

Or equivalent using clock_gettime directly.

sobomax commented 5 years ago

No it supports storing nanoseconds on disk, I use this feature:

/usr/sbin/tcpdump -i XXX -j adapter --time-stamp-precision=nano

Ah, ok. Good to know, thanks!

sobomax commented 5 years ago

If you want to have very accurate replay you probably want to use a busy loop instead of sleeping:

while (std::chrono::system_time::now() < deadline);

Or equivalent using clock_gettime directly.

Yeah, but that would be unnecessary and even detrimental in our use case - we spin udpreplay as an auxiliary traffic generator for automated functionality test suite in our RTC software (rtpproxy). As such, we are not so much care about jitter around single packet, but overall packet rate should be only function of the system timer rate. We also spin several instances of it and do other stuff, so with busy-wait scheme that would grind our test instances to halt.

Therefore this is a bit out of scope of this particular PR.

sobomax commented 5 years ago

@rigtorp new version is in using nano precision mode of libpcap. I've also went in and reworked "-c xxx" case to use the new algorithm. Test case has been added to make sure both modes work as expected. While there, I have changed option validation to allow zero delay between packets, which may be useful for performance testing. Please let me know if you have any more suggestions, thanks!

rigtorp commented 4 years ago

I'm on vacation will review soon.

rigtorp commented 4 years ago

@sobomax Did you take a look at #20 ?

sobomax commented 4 years ago

@rigtorp I don't think this is going anywhere (i.e. see PR #20), so I just stick with my changes. Thanks!