appneta / tcpreplay

Pcap editing and replay tools for *NIX and Windows - Users please download source from
http://tcpreplay.appneta.com/wiki/installation.html#downloads
1.19k stars 268 forks source link

[Bug] 64 bit rollover causes pps replay to switch to max rate fairly quickly #629

Closed p-sandie closed 3 years ago

p-sandie commented 3 years ago

64 bit rollover causes pps replay to switch to max rate fairly quickly, due to code below in calc_sleep_time() (in send_packets.c):

 /*
  * packets * 1000000 divided by pps = microseconds
  * packets per sec (pps) = packets per hour / (60 * 60)
  */
 COUNTER next_tx_us = (pkts_sent * 1000000) * (60 * 60) / pph;
 COUNTER tx_us = now_us - start_us;

The use of an absolute microseconds value means that the numerator in the next_ts_us calculation rolls over at max COUNTER / 3.6 billion, e.g. at 400Kpps that's around three and half hours.

That results in the next packet being sent immediately, so effectively a switch to full rate.

Maybe worth moving away from microseconds there, e.g. use seconds and microseconds, however a quick fix is changing to e.g.:

 COUNTER next_tx_us = pkts_sent * 3600 /(pph / 1000000);

At 400Kpps that allows over 400 years.

fklassen commented 3 years ago
 COUNTER next_tx_us = pkts_sent * 3600 /(pph / 1000000);

This formula does not work for small value pps. For example, 200 pps will cause a divide-by-zero core dump. Current formula is a compromise between low value and high value support.

p-sandie commented 3 years ago

Sorry my lack of foresight, in solving only my immediate issue.

The obvious solution of simply using either the calculation as it stands "(pkts_sent 1000000) (60 * 60) / pph" where pph < 1000000, or the updated calculation otherwise - so to avoid the divide by zero - just highlights a problem with having moved the division to the denominator - so the calculation suffers from inaccuracy due to integer division, fairly quickly also.

Possibly this may be a better compromise:

COUNTER next_tx_us = (pph < 1000000) ? ((pkts_sent 1000000) (60 60) / pph) : (pkts_sent 1000000)/(pph / 3600);

That avoids the divide-by-zero, and by having the division in the denominator such that it doesn't suffer from the integer division issues (i.e. by having something there by which pph is always divisible), also gets round that issue.

I did write a small test harness using the above to get the calculated next_tx_us value for pps rates of 1,10,100,1000,100K,400K,1 million at 1 minute, 1 hour, 1 day, 1 week, 1 month (31 days) and 1 year (365 days).

Output below shows "original calculation overflow - new calculation still good" where using the above fixes the issue that would occur if the current calculation remains, and "both original and updated calculation overflow" where overflow breaks either calculation.

With this method it's good for at least a year at 400Kpps, and breaks somewhere between a month and a year at 1Mpps. With the original method overflow breaks the calculation within a day at 400Kpps and 1Mpps (from previous at ~3.5 hours and ~1.4 hours respectively)

Does go a bit further towards supporting both low and high pps rates, and have done a bit testing on it this time round.

PPS RATE: 1 For pkts_sent 60 (minute) next_tx_us is 60000000 using amended calculation with pps set at 1, original calculation gave 60000000 For pkts_sent 3600 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 1, original calculation gave 3600000000 For pkts_sent 86400 (day) next_tx_us is 86400000000 using amended calculation with pps set at 1, original calculation gave 86400000000 For pkts_sent 604800 (week) next_tx_us is 604800000000 using amended calculation with pps set at 1, original calculation gave 604800000000 For pkts_sent 2678400 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 1, original calculation gave 2678400000000 For pkts_sent 31536000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 1, original calculation gave 31536000000000 PPS RATE: 10 For pkts_sent 600 (minute) next_tx_us is 60000000 using amended calculation with pps set at 10, original calculation gave 60000000 For pkts_sent 36000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 10, original calculation gave 3600000000 For pkts_sent 864000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 10, original calculation gave 86400000000 For pkts_sent 6048000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 10, original calculation gave 604800000000 For pkts_sent 26784000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 10, original calculation gave 2678400000000 For pkts_sent 315360000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 10, original calculation gave 31536000000000 PPS RATE: 100 For pkts_sent 6000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 100, original calculation gave 60000000 For pkts_sent 360000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 100, original calculation gave 3600000000 For pkts_sent 8640000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 100, original calculation gave 86400000000 For pkts_sent 60480000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 100, original calculation gave 604800000000 For pkts_sent 267840000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 100, original calculation gave 2678400000000 For pkts_sent 3153600000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 100, original calculation gave 31536000000000 PPS RATE: 1000 For pkts_sent 60000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 1000, original calculation gave 60000000 For pkts_sent 3600000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 1000, original calculation gave 3600000000 For pkts_sent 86400000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 1000, original calculation gave 86400000000 For pkts_sent 604800000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 1000, original calculation gave 604800000000 For pkts_sent 2678400000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 1000, original calculation gave 2678400000000 For pkts_sent 31536000000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 1000, original calculation gave 791426543817 original calculation overflow - new calculation still good PPS RATE: 10000 For pkts_sent 600000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 10000, original calculation gave 60000000 For pkts_sent 36000000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 10000, original calculation gave 3600000000 For pkts_sent 864000000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 10000, original calculation gave 86400000000 For pkts_sent 6048000000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 10000, original calculation gave 92390442396 original calculation overflow - new calculation still good For pkts_sent 26784000000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 10000, original calculation gave 116352211984 original calculation overflow - new calculation still good For pkts_sent 315360000000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 10000, original calculation gave 279016986214 original calculation overflow - new calculation still good PPS RATE: 100000 For pkts_sent 6000000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 100000, original calculation gave 60000000 For pkts_sent 360000000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 100000, original calculation gave 3600000000 For pkts_sent 8640000000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 100000, original calculation gave 35159044239 original calculation overflow - new calculation still good For pkts_sent 60480000000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 100000, original calculation gave 41149486636 original calculation overflow - new calculation still good For pkts_sent 267840000000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 100000, original calculation gave 13870300464 original calculation overflow - new calculation still good For pkts_sent 3153600000000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 100000, original calculation gave 22812207412 original calculation overflow - new calculation still good PPS RATE: 400000 For pkts_sent 24000000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 400000, original calculation gave 60000000 For pkts_sent 1440000000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 400000, original calculation gave 3600000000 For pkts_sent 34560000000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 400000, original calculation gave 9538566359 original calculation overflow - new calculation still good For pkts_sent 241920000000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 400000, original calculation gave 2718769816 original calculation overflow - new calculation still good For pkts_sent 1071360000000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 400000, original calculation gave 1060061524 original calculation overflow - new calculation still good For pkts_sent 12614400000000 (year) next_tx_us is 31536000000000 using amended calculation with pps set at 400000, original calculation gave 10001968472 original calculation overflow - new calculation still good PPS RATE: 1000000 For pkts_sent 60000000 (minute) next_tx_us is 60000000 using amended calculation with pps set at 1000000, original calculation gave 60000000 For pkts_sent 3600000000 (hour) next_tx_us is 3600000000 using amended calculation with pps set at 1000000, original calculation gave 3600000000 For pkts_sent 86400000000 (day) next_tx_us is 86400000000 using amended calculation with pps set at 1000000, original calculation gave 4414470783 original calculation overflow - new calculation still good For pkts_sent 604800000000 (week) next_tx_us is 604800000000 using amended calculation with pps set at 1000000, original calculation gave 156722028 original calculation overflow - new calculation still good For pkts_sent 2678400000000 (month) next_tx_us is 2678400000000 using amended calculation with pps set at 1000000, original calculation gave 3622109312 original calculation overflow - new calculation still good For pkts_sent 31536000000000 (year) next_tx_us is 13089255926290 using amended calculation with pps set at 1000000, original calculation gave 2315825108 both original and updated calculation overflow

fklassen commented 3 years ago

COUNTER next_tx_us = (pph < 1000000) ? ((pkts_sent 1000000) (60 60) / pph) : (pkts_sent 1000000)/(pph / 3600);

Thanks for doing the work for me. I was playing around with a similar formula.

Another issue with your first formula was that pps values around 500 would suffer some resolution issues, causing packets to be sent in small bursts. Your new formula addresses this.

Code style I prefer this which compiles to the same code:

COUNTER next_tx_us = (pph < 1000000) ? ((pkts_sent * 1000000) * (60 * 60) / pph) : (pkts_sent * 1000000)/(pph / (60 * 60));
fklassen commented 3 years ago

Resolved in PR #644

Decision on which calculation to use will be based on how close we are to overflowing. Disadvantage of updated calculation is that it doesn't work for very low pps (e.g. --pps=0.1) however that is highly unlikely to result in an overflow.

Also new formula is updated to do 2 divisions which should improve integer accuracy.

next_tx_us = (pkts_sent * 1000000) / pph / (60 * 60);