Skip to content

Commit

Permalink
rawnetworkinterfacedriver: Implement live streaming
Browse files Browse the repository at this point in the history
Adds support for live streaming of captured packets such that they can
be processed in real time by tests instead of needing to capture for a
set period of time time and do post-processing.

As an example:

```python
import dpkt

drv = target.get_driver("RawNetworkInterfaceDriver")

with drv.record(None, timeout=60) as p:
    pcap = dpkt.pcap.Reader(p.stdout)
    for timestamp, buf in pcap:
        eth = dpkt.ethernet.Ethernet(buf)
        ....
```

Signed-off-by: Joshua Watt <[email protected]>
  • Loading branch information
JoshuaWatt committed Apr 22, 2024
1 parent 7529280 commit fdeb30b
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
4 changes: 4 additions & 0 deletions helpers/labgrid-raw-interface
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ def main(program, ifname, count, timeout=0):
if program == "tcpdump":
args.append("-n")
args.append(f"--interface={ifname}")
# Write out each packet as it is received
args.append("--packet-buffered")
# Capture complete packets (for compatibility with older tcpdump versions)
args.append("--snapshot-length=0")
args.append("-w")
args.append('-')

Expand Down
23 changes: 17 additions & 6 deletions labgrid/driver/rawnetworkinterfacedriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def start_record(self, filename, *, count=None, timeout=None):
Starts tcpdump on bound network interface resource.
Args:
filename (str): name of a file to record to
filename (str): name of a file to record to, or None to record to stdout
count (int): optional, exit after receiving this many number of packets
timeout (int): optional, number of seconds to capture packets before tcpdump exits
Returns:
Expand All @@ -74,8 +74,11 @@ def start_record(self, filename, *, count=None, timeout=None):
cmd.append("--timeout")
cmd.append(str(timeout))
cmd = self._wrap_command(cmd)
with open(filename, "wb") as outdata:
self._record_handle = subprocess.Popen(cmd, stdout=outdata, stderr=subprocess.PIPE)
if filename is None:
self._record_handle = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
else:
with open(filename, "wb") as outdata:
self._record_handle = subprocess.Popen(cmd, stdout=outdata, stderr=subprocess.PIPE)
return self._record_handle

@Driver.check_active
Expand All @@ -101,18 +104,26 @@ def record(self, filename, *, count=None, timeout=None):
Either count or timeout must be specified.
Args:
filename (str): name of a file to record to
filename (str): name of a file to record to, or None to live stream packets
count (int): optional, exit after receiving this many number of packets
timeout (int): optional, number of seconds to capture packets before tcpdump exits
Returns:
Popen object of tcpdump process.
Popen object of tcpdump process. If filename is None, packets can be read from stdout
"""
assert count or timeout

try:
yield self.start_record(filename, count=count, timeout=timeout)
finally:
self.stop_record()
# If live streaming packets, there is no reason to wait for tcpdump
# to finish, so terminate it as soon as the context is left
try:
self.stop_record(timeout=0 if filename is None else None)
except subprocess.TimeoutExpired:
# A timeout is expected if the context is exited early while
# piping to stdout
if filename is not None:
raise

@Driver.check_active
@step(args=["filename"])
Expand Down

0 comments on commit fdeb30b

Please sign in to comment.