A Python binding for WinDivert driver
See the codePyDivert is a high-performance, cross-platform Python binding for capturing, modifying, and dropping network packets. It supports Windows via WinDivert and Linux via eBPFDivert, an eBPF implementation of the WinDivert API. Both backends use the same filter language, layers, flags and packet metadata.
[!WARNING] Linux support is experimental. eBPFDivert is still at 0.0.x, so expect rough edges and please report issues. Windows support is stable.
asyncio and Structural Pattern Matching (PEP 634).libebpfdivert.so.[!NOTE] Windows Server is currently untested but likely works if it meets the architecture requirements. On Linux, root or
CAP_BPF+CAP_NET_ADMIN+CAP_NET_RAWis enough.
Install PyDivert using pip:
pip install pydivert
Or using uv:
uv add pydivert
The same command works on Windows and Linux; the platform wheel carries the native backend.
The main entry points are pydivert.Divert for cross-platform capturing and pydivert.Packet for manipulation.
[!TIP] All code examples in this README are verified by automated integration tests in
pydivert/tests/test_readme_examples.py.
import pydivert
# Capture only TCP packets to port 80 (HTTP requests)
with pydivert.Divert("tcp.DstPort == 80") as diverter:
for packet in diverter:
print(f"Captured: {packet}")
diverter.send(packet) # Re-inject the packet back into the stack
When you call .recv() (or iterate over the capture object), the packet is taken out of the network stack. It will not reach its destination unless you explicitly call .send(packet).
asyncio SupportPyDivert 4.0 supports asyncio natively using modern async with and async for syntax.
import asyncio
import pydivert
async def main():
# Asynchronously capture packets
async with pydivert.Divert("tcp.DstPort == 80") as diverter:
async for packet in diverter:
print(f"Async captured: {packet}")
await diverter.send_async(packet)
if __name__ == "__main__":
asyncio.run(main())
Filter and analyze packets using clean match/case syntax.
import pydivert
from pydivert.packet import Packet
from pydivert.packet.tcp import TCPHeader
with pydivert.Divert("tcp") as diverter:
for packet in diverter:
match packet:
case Packet(tcp=TCPHeader(dst_port=80)):
print("HTTP Traffic")
case Packet(tcp=TCPHeader(dst_port=443)):
print("HTTPS Traffic")
diverter.send(packet)
By simply not calling .send(packet), the packet is effectively dropped.
import pydivert
# Block all traffic from a specific IP address
with pydivert.Divert("ip.SrcAddr == 1.2.3.4") as diverter:
for packet in diverter:
print(f"Blocking packet from {packet.src_addr}")
# Packet is dropped here
You can inspect or modify the raw bytes of the packet payload.
import pydivert
# Filter for TCP packets with payload
with pydivert.Divert("tcp.PayloadLength > 0") as diverter:
for packet in diverter:
if b"secret-token" in packet.payload:
# Redact the token
packet.payload = packet.payload.replace(b"secret-token", b"REDACTED")
diverter.send(packet)
PyDivert can verify and recalculate network checksums automatically.
packet.is_checksum_valid: Returns True if all checksums (IP, TCP, UDP, ICMP) in the packet are correct.packet.recalculate_checksums(): Recalculates all checksums based on the current header and payload values.if not packet.is_checksum_valid:
print("Corrupted packet detected!")
packet.recalculate_checksums()
The pydivert.Packet object provides easy access to common fields:
packet.src_addr, packet.dst_addr, packet.ip.ttl, packet.ip.protocolpacket.src_port, packet.dst_port, packet.tcp.flagspacket.payload (bytes)timestamp: Capture time (QueryPerformanceCounter ticks on Windows, CLOCK_MONOTONIC nanoseconds on Linux).is_loopback, is_impostor, is_sniffed: Boolean flags.interface: Index of the capture interface.direction: Direction.INBOUND or Direction.OUTBOUND.Detailed protocol headers are available through packet.ipv4, packet.ipv6, packet.tcp, packet.udp, and packet.icmp.
Layer.NETWORK (default): IP packets to and from the local host.Layer.NETWORK_FORWARD: IP packets being routed through the host.Layer.FLOW: Connection established/deleted events.Layer.SOCKET: Socket bind/connect/listen/accept/close events.Layer.REFLECT: Divert handles opened and closed on the system.See the Linux Backend Guide for how these are implemented on Linux, and the few differences.
Flag.SNIFF: Monitor mode (sniffing).Flag.DROP: Drop packets by default.Flag.FRAGMENTS: Capture all IP fragments.Flag.RECV_ONLY / Flag.SEND_ONLY: Restricted handles.Divert uses the WinDivert filter language to select which packets to capture. For a detailed reference on the syntax and available fields, see the Filter Language Guide.
For the original technical reference, please visit the official WinDivert documentation.
| Divert | Backend |
|---|---|
| 4.0.0+ | WinDivert 2.2.2 (bundled) / eBPFDivert 0.0.5 (bundled) |
| 3.0.0+ | WinDivert 2.2.2 (bundled) - Full support for modern metadata and layers |
uv sync --extra test --extra docsuv run pytestPyDivert includes a Vagrantfile to easily run tests in a clean environment with the necessary privileges.
To run tests on a Windows 11 VM:
vagrant up windows
# This will sync the project and run tests automatically
vagrant provision windows --provision-with test-windows
To run tests on a Ubuntu VM with eBPF support:
vagrant up linux
# This will run the test suite as root automatically
vagrant provision linux --provision-with test-linux
vagrant destroy -f linux
To test against a local eBPFDivert build instead of the pinned release, set PYDIVERT_EBPFDIVERT_LOCAL=/path/to/libebpfdivert.so before scripts/fetch_binaries.py, or PYDIVERT_EBPFDIVERT_LIB at runtime.
The full API documentation is available at https://ffalcinelli.github.io/pydivert/.
PyDivert is dual-licensed under LGPL-3.0-or-later and GPL-2.0-or-later.
PyDivert is committed to security and uses Snyk for continuous vulnerability scanning. For more details on our security practices and how to report vulnerabilities, please refer to the Security Policy.
Overall
Maintained
Code review
PyPI
Latest release
Releases this year
798 followers · starred Jul 2018
A Python binding for WinDivert driver
See the codePyDivert is a high-performance, cross-platform Python binding for capturing, modifying, and dropping network packets. It supports Windows via WinDivert and Linux via eBPFDivert, an eBPF implementation of the WinDivert API. Both backends use the same filter language, layers, flags and packet metadata.
[!WARNING] Linux support is experimental. eBPFDivert is still at 0.0.x, so expect rough edges and please report issues. Windows support is stable.
asyncio and Structural Pattern Matching (PEP 634).libebpfdivert.so.[!NOTE] Windows Server is currently untested but likely works if it meets the architecture requirements. On Linux, root or
CAP_BPF+CAP_NET_ADMIN+CAP_NET_RAWis enough.
Install PyDivert using pip:
pip install pydivert
Or using uv:
uv add pydivert
The same command works on Windows and Linux; the platform wheel carries the native backend.
The main entry points are pydivert.Divert for cross-platform capturing and pydivert.Packet for manipulation.
[!TIP] All code examples in this README are verified by automated integration tests in
pydivert/tests/test_readme_examples.py.
import pydivert
# Capture only TCP packets to port 80 (HTTP requests)
with pydivert.Divert("tcp.DstPort == 80") as diverter:
for packet in diverter:
print(f"Captured: {packet}")
diverter.send(packet) # Re-inject the packet back into the stack
When you call .recv() (or iterate over the capture object), the packet is taken out of the network stack. It will not reach its destination unless you explicitly call .send(packet).
asyncio SupportPyDivert 4.0 supports asyncio natively using modern async with and async for syntax.
import asyncio
import pydivert
async def main():
# Asynchronously capture packets
async with pydivert.Divert("tcp.DstPort == 80") as diverter:
async for packet in diverter:
print(f"Async captured: {packet}")
await diverter.send_async(packet)
if __name__ == "__main__":
asyncio.run(main())
Filter and analyze packets using clean match/case syntax.
import pydivert
from pydivert.packet import Packet
from pydivert.packet.tcp import TCPHeader
with pydivert.Divert("tcp") as diverter:
for packet in diverter:
match packet:
case Packet(tcp=TCPHeader(dst_port=80)):
print("HTTP Traffic")
case Packet(tcp=TCPHeader(dst_port=443)):
print("HTTPS Traffic")
diverter.send(packet)
By simply not calling .send(packet), the packet is effectively dropped.
import pydivert
# Block all traffic from a specific IP address
with pydivert.Divert("ip.SrcAddr == 1.2.3.4") as diverter:
for packet in diverter:
print(f"Blocking packet from {packet.src_addr}")
# Packet is dropped here
You can inspect or modify the raw bytes of the packet payload.
import pydivert
# Filter for TCP packets with payload
with pydivert.Divert("tcp.PayloadLength > 0") as diverter:
for packet in diverter:
if b"secret-token" in packet.payload:
# Redact the token
packet.payload = packet.payload.replace(b"secret-token", b"REDACTED")
diverter.send(packet)
PyDivert can verify and recalculate network checksums automatically.
packet.is_checksum_valid: Returns True if all checksums (IP, TCP, UDP, ICMP) in the packet are correct.packet.recalculate_checksums(): Recalculates all checksums based on the current header and payload values.if not packet.is_checksum_valid:
print("Corrupted packet detected!")
packet.recalculate_checksums()
The pydivert.Packet object provides easy access to common fields:
packet.src_addr, packet.dst_addr, packet.ip.ttl, packet.ip.protocolpacket.src_port, packet.dst_port, packet.tcp.flagspacket.payload (bytes)timestamp: Capture time (QueryPerformanceCounter ticks on Windows, CLOCK_MONOTONIC nanoseconds on Linux).is_loopback, is_impostor, is_sniffed: Boolean flags.interface: Index of the capture interface.direction: Direction.INBOUND or Direction.OUTBOUND.Detailed protocol headers are available through packet.ipv4, packet.ipv6, packet.tcp, packet.udp, and packet.icmp.
Layer.NETWORK (default): IP packets to and from the local host.Layer.NETWORK_FORWARD: IP packets being routed through the host.Layer.FLOW: Connection established/deleted events.Layer.SOCKET: Socket bind/connect/listen/accept/close events.Layer.REFLECT: Divert handles opened and closed on the system.See the Linux Backend Guide for how these are implemented on Linux, and the few differences.
Flag.SNIFF: Monitor mode (sniffing).Flag.DROP: Drop packets by default.Flag.FRAGMENTS: Capture all IP fragments.Flag.RECV_ONLY / Flag.SEND_ONLY: Restricted handles.Divert uses the WinDivert filter language to select which packets to capture. For a detailed reference on the syntax and available fields, see the Filter Language Guide.
For the original technical reference, please visit the official WinDivert documentation.
| Divert | Backend |
|---|---|
| 4.0.0+ | WinDivert 2.2.2 (bundled) / eBPFDivert 0.0.5 (bundled) |
| 3.0.0+ | WinDivert 2.2.2 (bundled) - Full support for modern metadata and layers |
uv sync --extra test --extra docsuv run pytestPyDivert includes a Vagrantfile to easily run tests in a clean environment with the necessary privileges.
To run tests on a Windows 11 VM:
vagrant up windows
# This will sync the project and run tests automatically
vagrant provision windows --provision-with test-windows
To run tests on a Ubuntu VM with eBPF support:
vagrant up linux
# This will run the test suite as root automatically
vagrant provision linux --provision-with test-linux
vagrant destroy -f linux
To test against a local eBPFDivert build instead of the pinned release, set PYDIVERT_EBPFDIVERT_LOCAL=/path/to/libebpfdivert.so before scripts/fetch_binaries.py, or PYDIVERT_EBPFDIVERT_LIB at runtime.
The full API documentation is available at https://ffalcinelli.github.io/pydivert/.
PyDivert is dual-licensed under LGPL-3.0-or-later and GPL-2.0-or-later.
PyDivert is committed to security and uses Snyk for continuous vulnerability scanning. For more details on our security practices and how to report vulnerabilities, please refer to the Security Policy.
Overall
Maintained
Code review
PyPI
Latest release
Releases this year
798 followers · starred Jul 2018