Skip to content

Feat/12236 packet count v4 - #43

Open
Davihan11 wants to merge 8 commits into
mainfrom
feat/12236-packet-count-v4
Open

Feat/12236 packet count v4#43
Davihan11 wants to merge 8 commits into
mainfrom
feat/12236-packet-count-v4

Conversation

@Davihan11

Copy link
Copy Markdown
Collaborator

This pull request introduces significant improvements to the TRex STL (Stateless) traffic generator integration, focusing on enhanced multi-PCAP handling and the addition of an exact packet count (burst) mode. The changes improve both the usability and determinism of traffic generation for testing. The most important changes are grouped below.

STL Mode Enhancements

  • Multi-PCAP support: When multiple PCAPs are supplied to an STL profile, they are now automatically merged into a single interleaved PCAP (with packets mixed proportionally to their weights) and replayed together, rather than sequentially. This ensures more realistic traffic mixes and simplifies test setup.
  • Exact-count (burst) mode: Added support for an STL "exact packet count" mode, which sends a fixed number of packets at a specified rate using the --trex-stl-burst option. This enables deterministic testing where you need to send precisely N packets. The packet count is fixed, but the send rate is scaled by the traffic multiplier, supporting binary search and enumeration.

Codebase Refactoring and Usability

  • Refactored STL/PCAP handling: Introduced a Pcap NamedTuple for better type safety and clarity, and updated all STL/ASTF/STF profile logic to use this structure. PCAP upload, merging, and replay logic is now more robust and easier to maintain.
  • Added BaseAdHocTrex class: Provides a unified base for ad-hoc STL profiles that receive their PCAPs at runtime, ensuring consistent behavior across test profiles.

Documentation and Developer Experience

  • Updated documentation: The README.md has been expanded to describe multi-PCAP merging, the new STL exact-count mode, and usage examples for both. It also documents the new --force-trex-mode and --prefer-trex-mode options for test selection.

STL_EXACT was never passed to the TRex client manager (AdHocExactStlProfile
always constructed the base manager with mode=STL), so it only served to
select a separate profile class in the tests. Replace it with a dedicated
--trex-exact-count flag that is decoupled from mode selection: the mode stays
STL and the exact packet count is handled inside STL's run().

- Remove STL_EXACT from the TrexMode enum and str_to_trex_mode()
- Drop 'stl.exact' from --force-trex-mode / --prefer-trex-mode choices
- Add --trex-exact-count (store_true) pytest option
Move the STLTXSingleBurst exact-count logic from the AdHocExactStlProfile
subclass into the base STL branch of run(). When --trex-exact-count is set,
run() builds STLStream/STLTXSingleBurst streams (pps = --trex-pps * multiplier,
total_pkts = --trex-total-packets) instead of the duration-based push_remote
loop. For non-STL modes the flag is ignored with a warning.

- Delete ad_hoc_exact_stl_profile.py
- Store request on the base manager so run() can read pytest options
- Simplify all 'STL | STL_EXACT' case branches to 'STL'
Remove the AdHocExactStlProfile branches and always instantiate the normal
profile class. The exact packet count is now enabled via the --trex-exact-count
flag handled centrally in STL's run(), so the tests no longer need to branch on
a separate mode. STL is added to the available modes where it was previously
only reachable through STL_EXACT.
- Add -ec | --trex-exact-count flag to pytest_start.sh and pass it through
- Reword -pps / -tp help to reference the STL exact-count mode
- Update README to describe exact packet count as an STL option enabled by
  --trex-exact-count instead of a separate stl.exact mode
argparse interprets % as a format character in help strings, so the literal
<drop_rate%> in the --binary-search help raised 'unsupported format character'
when rendering --help. Escape it as %%.
…eparators

- Move merge_pcaps() from trex_client_manager.py to util/trex_util.py so it
  lives with the other TRex helpers.
- Introduce a BaseAdHocTrex parent subclass in trex_client_manager.py that
  owns the shared ad-hoc __init__ (runtime pcaps + STL mode); AdHocStlProfile
  now inherits it instead of duplicating the constructor.
- Use underscore separators for large numeric literals (200_000, 10_000_000)
  in conftest.py for readability.
Address PR review feedback for the packet-count feature:

- Replace the three separate options (--trex-exact-count, --trex-pps,
  --trex-total-packets) with a single --trex-stl-burst [PPS] [PACKET_COUNT]
  option (and -sb in pytest_start.sh). Defaults: 200000 PPS, 10000000 packets.
- Stream pcap merging in merge_pcaps() (PcapReader/PcapWriter) so large pcaps
  are not loaded fully into memory.
- Introduce a Pcap NamedTuple in trex_client_manager.py and use per-test unique
  merged pcap names to avoid overwrites/races.
- Fix trex client class imports to go through the 'trex' alias so isinstance()
  checks in STLClient.add_streams() keep working.
- Add ruff pre-commit config and ignore runtime-generated merged pcaps.
- Update README and param_template accordingly.
@Davihan11
Davihan11 requested review from matyas7dub and a lite review from Copilot August 14, 2026 07:16
@Davihan11 Davihan11 self-assigned this Aug 14, 2026
@Davihan11 Davihan11 added the enhancement New feature or request label Aug 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the Suricata-Test-Suite TRex integration by improving STL PCAP handling (multi-PCAP merging) and adding an STL exact packet-count (“burst”) mode to make traffic generation more deterministic and easier to control across runs and multiplier-based modes.

Changes:

  • Added streaming multi-PCAP merge support and integrated merged-PCAP behavior into STL profile initialization.
  • Introduced an STL exact-count mode controlled via --trex-stl-burst, plus corresponding CLI wiring and docs.
  • Expanded several test suites to allow selecting STL mode via --force-trex-mode/--prefer-trex-mode, and refactored ad-hoc STL profiles to share a common base.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
util/trex_util.py Adds merge_pcaps() helper to interleave multiple PCAPs into a single merged PCAP.
assets/trex/traffic_profiles/trex_client_manager.py Refactors PCAP representation, merges multi-PCAP STL profiles, and implements STL burst mode in run().
assets/trex/traffic_profiles/ad_hoc_stl_trex_profile.py Switches ad-hoc STL profile to reuse the new shared ad-hoc base class.
conftest.py Adds pytest option --trex-stl-burst and updates binary-search help text.
pytest_start.sh Adds -sb/--trex-stl-burst flag parsing and forwards it to pytest.
README.md Documents multi-PCAP STL merging, STL exact-count mode, and mode forcing/preference options.
param_template.py Adjusts MTU parameter list (removes 2500, leaves 3000).
tests/web_50_sites/test_web_50_sites.py Allows STL as a selectable TRex mode for this test.
tests/pcap_replay/test_pcap_replay.py Uses get_trex_mode to validate forced/preferred modes and passes explicit mode into profile.
tests/nfs_smb_simple/test_nfs_smb_simple.py Allows STL as a selectable TRex mode for this test.
tests/https_simple/test_https_simple.py Allows STL as a selectable TRex mode for this test.
tests/http_simple/test_http_simple.py Allows STL as a selectable TRex mode for this test.
tests/http_https_smb_simple/test_http_https_smb_simple.py Allows STL as a selectable TRex mode for this test.
.gitignore Ignores merged STL PCAPs generated at runtime.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread util/trex_util.py
Comment on lines +53 to +56
quotas = [w / total_w for w in weights]
min_q = min(q for q in quotas if q > 0)
# zero-weight sources emit nothing; others emit at least 1 packet per round
per_round = [0 if q <= 0 else max(1, round(q / min_q)) for q in quotas]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using weights with 10, 100, 1000. Not relevant at this scale.

Comment on lines +446 to +450
if total_pkts <= 0:
raise ValueError(
"--trex-stl-burst PACKET_COUNT must be > 0, "
f"got {total_pkts}"
)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why --trex-stl-burst is being parsed as floats in the first place.

Comment thread pytest_start.sh
Comment on lines +93 to +97
if [[ "$2" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
trex_stl_burst="$2"
shift
if [[ "$2" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
trex_stl_burst="$trex_stl_burst $2"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be made more consistent.

Comment on lines 161 to 165
if target_vlan != 0:
pcap_path = Path(edit_vlan(str(pcap_path), target_vlan))
self.pcaps[i] = (pcap_path.name, pcap[1])
self.pcaps[i] = Pcap(pcap_path, pcap.weight)
pcap_remote_path = self.get_remote_data_path(pcap_path)
send_to_remote(pcap_path, trex_hostname, pcap_remote_path)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more uniform and simpler this way. And it is just a one time cost on test start, not on test run.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the code correctly, the PCAPs are always merged if there is more than one, so this will always be redundant.

--trex-stl-burst is parsed as floats, but TRex requires an integer
packet count. Cast total_pkts to int while keeping pps as a float
(since pps is scaled by a float multiplier).
@Davihan11

Davihan11 commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

found a bug where trex refuses float packet values.
should be fixed with the latest commit

@Davihan11 Davihan11 added bug Something isn't working and removed bug Something isn't working labels Aug 14, 2026

@matyas7dub matyas7dub left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that default values for --trex-stl-burst should be handled in the argument parser along with validation if possible.
Also linting failed.

Comment thread conftest.py
help=(
"In STL mode, send a fixed burst of PACKET_COUNT packets at PPS "
"instead of replaying for the configured duration. With no "
"arguments, defaults to 200000 PPS and 10000000 packets. Only "

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, but it should be "200 000" and "10 000 000".

Comment thread pytest_start.sh
--remote-host="$suricata_server" --param-file="param.py" \
--trex-force-use \
$trex_mode_flags \
${trex_stl_burst_set:+--trex-stl-burst $trex_stl_burst} \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this conditionally includes the flag if trex_stl_burst_set is not empty, and if that's the case the extra_args array should be used instead.

Comment thread README.md
Comment on lines +268 to +283
defaults are used (200000 PPS, 10000000 packets):

| `pytest_start.sh` flag | pytest option | Description |
|---|---|---|
| `-sb [<PPS> <COUNT>]` | `--trex-stl-burst [<PPS> <PACKET_COUNT>]` | Send a fixed burst of `PACKET_COUNT` packets at `PPS` in STL mode (defaults: 200000 PPS, 10000000 packets) |

The packet count stays fixed, but the send rate (`pps`) is scaled by the traffic multiplier
(`effective_pps = PPS * multiplier`), so exact-count works with both multiplier
enumeration and binary search.

Examples:
```bash
# Send 1000 packets at 100 pps using STL exact-count mode
./pytest_start.sh -s claret -d http_simple -fm stl -f norules -sb 100 1000

# Use the default burst (200000 pps, 10000000 packets)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default values are mentioned 3 times.
This will almost certainly be changed at some point and forgotten about, so I wouldn't mention it anywhere outside of conftest.py.

Comment thread util/trex_util.py
total = 0
try:
with PcapWriter(str(out_path), append=False, sync=True) as writer:
while not all(exhausted):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This heuristic means that you can exhaust a short PCAP with a large weight very quickly, which will lead to the PCAP being under-represented relative to its expected weight.

Specifically I expect this to be an issue with Web50Sites, which has a DNS PCAP used this way.

I suggest merging the PCAPs until the result has the desired length and restarting any exhausted streams in the process. Though I would probably cap this at around 100K packets. (Or less, if there aren't any performance issues with TRex)

Comment on lines +480 to +501
# the pcaps can end early, so we loop through them
start = time()
elapsed = 0
pcap_index = 0
while elapsed < self.duration:
pcap = self.pcaps[pcap_index]
try:
client.push_remote(
pcap_filename=str(self.get_remote_data_path(pcap.path)),
ports=[0],
ipg_usec=self.BASE_IPG_USEC / pcap.weight,
speedup=self.multiplier,
count=1,
duration=int(self.duration - elapsed),
)
except TRexError:
# wait if port was not cleared yet
sleep(0.05)
elapsed = time() - start
continue
elapsed = time() - start
continue
elapsed = time() - start
pcap_index = (pcap_index + 1) % len(self.pcaps)
pcap_index = (pcap_index + 1) % len(self.pcaps)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified now that there is only one PCAP.

# scale the send rate by the traffic multiplier so that binary
# search (and multiplier enumeration) can vary the speed; the
# packet count stays fixed
multiplier = self.multiplier if self.multiplier is not None else 1.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already guarded on top of the function.

Comment on lines 161 to 165
if target_vlan != 0:
pcap_path = Path(edit_vlan(str(pcap_path), target_vlan))
self.pcaps[i] = (pcap_path.name, pcap[1])
self.pcaps[i] = Pcap(pcap_path, pcap.weight)
pcap_remote_path = self.get_remote_data_path(pcap_path)
send_to_remote(pcap_path, trex_hostname, pcap_remote_path)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand the code correctly, the PCAPs are always merged if there is more than one, so this will always be redundant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants