Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5e5612e
Add documentation on using packet manipulation module bf_pktpy
jafingerhut Feb 16, 2025
dbfb87e
Add more Python packages required when you want to use `bf-pktpy`
jafingerhut Feb 16, 2025
3acec26
Merge branch 'main' of https://github.com/p4lang/ptf into add-docs-on…
jafingerhut Feb 18, 2025
92527b9
Merge branch 'add-docs-on-using-bf-pktpy' of github.com:jafingerhut/p…
jafingerhut Feb 18, 2025
52566d6
Merge remote-tracking branch 'up/main' into add-docs-on-using-bf-pktpy
jafingerhut Feb 22, 2025
42311e3
Implement environment variable as one way to choose packet manip module
jafingerhut Feb 22, 2025
69487c9
Reformat Python code with black
jafingerhut Feb 22, 2025
f299e5c
Correct logic in program ptf for selecting pmm
jafingerhut Feb 23, 2025
120b520
Remove unnecessary white space change
jafingerhut Feb 23, 2025
61adee9
Fix CI build
jafingerhut Feb 23, 2025
6fa5d4f
Merge remote-tracking branch 'up/main' into add-docs-on-using-bf-pktpy
jafingerhut Feb 24, 2025
6274b3a
Corrections to README, and mention env var method of configuration
jafingerhut Feb 24, 2025
32ece7e
Make more function names usable via ptf when common in bf-pktpy and s…
jafingerhut Mar 6, 2025
7496fef
Merge branch 'add-docs-on-using-bf-pktpy' of github.com:jafingerhut/p…
jafingerhut Mar 6, 2025
8f529d1
Run black formatter on code.
jafingerhut Mar 6, 2025
9b0db69
More output during CI to help analyze failures
jafingerhut Mar 6, 2025
a7c0f92
Try using Python venv when running tests
jafingerhut Mar 6, 2025
7db732d
More debug output
jafingerhut Mar 6, 2025
e9f8950
Make each new shell use venv
jafingerhut Mar 6, 2025
9cef279
Eliminate some debug output
jafingerhut Mar 6, 2025
72ca744
Merge remote-tracking branch 'up/main' into add-docs-on-using-bf-pktpy
jafingerhut Mar 6, 2025
a8d6eb0
Merge branch 'add-docs-on-using-bf-pktpy' of github.com:jafingerhut/p…
jafingerhut Mar 6, 2025
4622182
Address review comment.
jafingerhut Mar 14, 2025
87e6c62
Fix typo
jafingerhut Mar 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The following software is required to run PTF:

Root/sudo privilege is required on the host, in order to run `ptf`.

The default packet manipulator tool for `ptf` is `Scapy`. To install it use:
The default packet manipulation module for `ptf` is `Scapy`. To install it use:
```text
pip install scapy==2.5.0
```
Expand All @@ -72,6 +72,94 @@ yum install tcpdump
apt-get install tcpdump
```

### Using `bf_pktpy` as an alternate packet manipulation module

The Python module `bf_pktpy` is included as part of the
Copy link
Contributor

Choose a reason for hiding this comment

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

We should consider making that module standalone or upstreaming it to PTF.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have a temporary repo with only the bf_pkpy code by itself here, and it does allow one to pip install it just fine in my testing: https://github.com/jafingerhut/bf-pktpy

I don't have a big opinion on whether open-p4studio should be modified to install it from a new https://github.com/p4lang/bf-pktpy repo, or if we end up with 2 copies of it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is easiest to vendor it as part of ptf, because it is specially built for this use case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Are you thinking an arrangement where pip install ptf installs code such that you can then do import ptf and/or import bf_pktpy?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, partially for convenience. Partially because bf_pktpy is specialized towards PTF and this coupling makes sure its functionality is scoped for PTF-style use cases.

`open-p4studio` repository:

+ https://github.com/p4lang/open-p4studio/tree/main/pkgsrc/ptf-modules/bf-pktpy

It was developed as an alternative to `scapy`. The tradeoffs of using
`bf_pktpy` vs. `scapy` are:

+ `scapy` implements more functionality, but is licensed under the
copyleft GNU General Public License v2.0 (see
https://github.com/secdev/scapy/blob/master/LICENSE), so may be
undesirable in use cases where you wish your tests to be released
under a different license.
+ `bf_pktpy` implements only a small subset of the functionality of
`scapy`, but it does include support for very commonly-used packet
headers. It is released under an Apache 2.0 license (see
https://github.com/p4lang/open-p4studio/blob/main/pkgsrc/ptf-modules/bf-pktpy/LICENSE).

The package `bf_pktpy` is not currently available from PyPI. To
install `bf_pktpy` from source code, choose one of these methods:

```bash
pip install "bf-pktpy@git+https://github.com/p4lang/open-p4studio#subdirectory=pkgsrc/ptf-utils/bf-pktpy"
```

```bash
git clone https://github.com/p4lang/open-p4studio
cd open-p4studio/pkgsrc/ptf-utils/bf-pktpy
pip install .
```

To make effective use of `bf_pktpy` you must also install these
additional Python packages. All are released under an MIT or
BSD-3-Clause license, which are compatible for releasing with
`bf_pktpy`, or for importing in a project with most licenses,
including `Apache-2.0`, `BSD-3-Clause`, `GPL-2.0-only`, or many
others.

```bash
pip install six getmac scapy_helper psutil
sudo apt-get install python3-dev
pip install netifaces
```

If you want to use `bf_pktpy` when running the command `ptf` from the
command line, provide the `-pmm` option as shown below.

```bash
ptf -pmm bf_pktpy.ptf.packet_pktpy <other command line arguments>
```

If you want to write a Python program that imports `ptf` and causes it
to use `bf_pktpy` instead of the default `scapy`, you can do so as
follows in your Python code:

```python
import ptf
ptf.config["packet_manipulation_module"] = "bf_pktpy.ptf.packet_pktpy"
import ptf.packet
```

The above methods are the highest precedence way of choosing the
packet manipulation module used by `ptf`. If you do not use those
methods, another way is to assign the packet manipulation module name
to the environment variable `PTF_PACKET_MANIPULATION_MODULE`, e.g. in
Bash:

```bash
export PTF_PACKET_MANIPULATION_MODULE="bf_pktpy.ptf.packet_pktpy"
```

When running such a program, you should see the following line printed
to standard output confirming that it is using `bf_pktpy` instead of
`scapy`:

```text
Using packet manipulation module: bf_pktpy.ptf.packet_pktpy
```

If instead you see this line of output, `ptf` is using `scapy`:

```text
Using packet manipulation module: ptf.packet_scapy
```


## Run PTF

Once you have written tests and your switch is running, you can run 'ptf'. Use
Expand Down
17 changes: 15 additions & 2 deletions ptf
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ config_default = {
"test_case_timeout": None,
# Socket options
"socket_recv_size": 4096,
# Packet manipulation provider module
"packet_manipulation_module": "ptf.packet_scapy",
# Other configuration
"port_map": None,
}
Expand Down Expand Up @@ -435,6 +433,21 @@ be subtracted from the result by prefixing them with the '^' character. """
config = config_default.copy()
for key in config.keys():
config[key] = getattr(args, key)
# For selecting the packet manipulation module when running the
# `ptf` command, the order of precedence is:
# (1) If the `--packet-manipulation-module` command line option is
# present, use its value.
# (2) Otherwise, if the environment variable
# PTF_PACKET_MANIPULATION_MODULE is defined, use its value.
# (3) Otherwise, use "ptf.packet_scapy"
pmm_key = "packet_manipulation_module"
if getattr(args, pmm_key):
# Then use the value from the command line option.
config[pmm_key] = getattr(args, pmm_key)
elif os.getenv("PTF_PACKET_MANIPULATION_MODULE"):
config[pmm_key] = os.getenv("PTF_PACKET_MANIPULATION_MODULE")
else:
Copy link
Contributor

Choose a reason for hiding this comment

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

elif?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated in commit 23

config[pmm_key] = "ptf.packet_scapy"

return (config, args)

Expand Down
36 changes: 30 additions & 6 deletions src/ptf/packet.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
"""A pluggable packet module

This module dynamically imports definitions from packet manipulation module,
specified in config or provided as an agrument.
specified in config or provided as an argument.
The default one is Scapy, but one can develop its own packet manipulation framework and
then, create an implementation of packet module for it (for Scapy it is packet_scapy.py)
"""

import os as _os
from ptf import config

__module = __import__(
config.get("packet_manipulation_module", "ptf.packet_scapy"), fromlist=["*"]
)
# When module ptf.packet is imported, this is the order of precedence for
# deciding which packet manipulation module is used:
# (1) If the key "packet_manipulation_module" is present in dict
# 'config', then its value should be a string, and it will be used
# as the name of the module to use.
# (2) Otherwise, if the environment variable
# PTF_PACKET_MANIPULATION_MODULE is defined, use its value.
# (3) Otherwise, the default module name "ptf.packet_scapy" is used.

# Note: Applications using ptf.packet can control the choice of packet
# manipulation module in any way they wish by doing the following:
#
# import ptf
# ptf.config["packet_manipulation_module"] = my_app_choice_of_pmm
# import ptf.packet

if "packet_manipulation_module" in config:
_packet_manipulation_module = config["packet_manipulation_module"]
else:
_env_val = _os.getenv("PTF_PACKET_MANIPULATION_MODULE")
if _env_val:
_packet_manipulation_module = _env_val
else:
_packet_manipulation_module = "ptf.packet_scapy"

__module = __import__(_packet_manipulation_module, fromlist=["*"])
__keys = []

# import logic - everything from __all__ if provided, otherwise everything not starting
# with underscore
# import logic - everything from __all__ if provided, otherwise
# everything not starting with underscore.
print("Using packet manipulation module: %s" % __module.__name__)
if "__all__" in __module.__dict__:
__keys = __module.__dict__["__all__"]
Expand Down
29 changes: 29 additions & 0 deletions src/ptf/packet_scapy.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import scapy.packet
import scapy.main
import scapy.fields
import scapy.sendrecv
import scapy.utils

if not config.get("disable_ipv6", False):
Expand Down Expand Up @@ -151,3 +152,31 @@ def mysummary(self):
# Scapy has its own hexdump
hexdump = scapy.utils.hexdump
ls = scapy.packet.ls

# The names below are assigned here so that, like the other names
# above, they can be used by importers of the ptf.packet module as if
# they were defined inside of ptf.packet, and they are commonly
# available as ptf.packet.<name> regardless whether you use scapy or
# bf-pktpy as the packet manipulation module.
#
# Commented-out lines are so because they have no equivalent function
# implemented in bf-pktpy yet.

# sndrcv = scapy.sendrecv.sndrcv
send = scapy.sendrecv.send
sendp = scapy.sendrecv.sendp
# sendpfast = scapy.sendrecv.sendpfast
sr = scapy.sendrecv.sr
sr1 = scapy.sendrecv.sr1
srp = scapy.sendrecv.srp
srp1 = scapy.sendrecv.srp1
srloop = scapy.sendrecv.srloop
srploop = scapy.sendrecv.srploop
# sndrcvflood = scapy.sendrecv.sndrcvflood
# srflood = scapy.sendrecv.srflood
# sr1flood = scapy.sendrecv.sr1flood
# srpflood = scapy.sendrecv.srpflood
# srp1flood = scapy.sendrecv.srp1flood
sniff = scapy.sendrecv.sniff
bridge_and_sniff = scapy.sendrecv.bridge_and_sniff
# tshark = scapy.sendrecv.tshark