mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-30 20:43:02 +02:00
test: netns: Add packet capture helper
This tries to improve on the existing sniffer in a couple of ways: - In addition to being usable as a context manager, also allow explicitly starting and stopping the capture. - Try to synchronize the start of the capture by snooping stderr and wait for tcpdump to start up. This should eliminate the need for sleeps before starting to send packets. - By default, execute a safety sleep when terminating the capture to ensure that any straggling packets have had time to arrive at their destination.
This commit is contained in:
@@ -2,7 +2,9 @@ import ctypes
|
||||
import multiprocessing
|
||||
import os
|
||||
import random
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from . import env
|
||||
@@ -204,3 +206,69 @@ class IsolatedMacVlan:
|
||||
|
||||
def must_not_receive(self, *args, **kwargs):
|
||||
self.must_receive(*args, **kwargs, must=False)
|
||||
|
||||
def pcap(self, expr, ifname=None):
|
||||
ifname = ifname if ifname else self.ifname
|
||||
return Pcap(self, ifname, expr)
|
||||
|
||||
class Pcap:
|
||||
def __init__(self, netns, ifname, expr):
|
||||
self.netns, self.ifname, self.expr = netns, ifname, expr
|
||||
self.pcap = tempfile.NamedTemporaryFile(suffix=".pcap", delete=False)
|
||||
self.proc = None
|
||||
|
||||
def __del__(self):
|
||||
self.pcap.close()
|
||||
os.unlink(self.pcap.name)
|
||||
|
||||
def __enter__(self):
|
||||
self.start()
|
||||
return self
|
||||
|
||||
def __exit__(self, _, __, ___):
|
||||
self.stop()
|
||||
|
||||
def start(self):
|
||||
assert self.proc == None, "Can't start an already running Pcap"
|
||||
|
||||
argv = f"tshark -ln -i {self.ifname} -w {self.pcap.name} {self.expr}".split()
|
||||
self.proc = self.netns.popen(argv,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
|
||||
while " -- Capture started." not in self.proc.stderr.readline():
|
||||
pass
|
||||
|
||||
print("Capture running")
|
||||
|
||||
def stop(self, sleep=3):
|
||||
assert self.proc, "Can't stop an already stopped Pcap"
|
||||
|
||||
if sleep:
|
||||
# In the common case, stop() will be called right after
|
||||
# the final packet of whatever we're testing has just been
|
||||
# sent. Therefore, allow for some time to pass before
|
||||
# terminating the capture.
|
||||
time.sleep(sleep)
|
||||
|
||||
self.proc.send_signal(signal.SIGUSR2)
|
||||
self.proc.terminate()
|
||||
try:
|
||||
_, stderr = self.proc.communicate(5)
|
||||
print(stderr)
|
||||
return
|
||||
except subprocess.TimeoutExpired:
|
||||
try:
|
||||
self.proc.kill()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
self.proc.wait()
|
||||
|
||||
def tcpdump(self, args=""):
|
||||
tcpdump = subprocess.run((f"tcpdump -r {self.pcap.name} -n " + args).split(),
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
text=True, check=True)
|
||||
return tcpdump.stdout
|
||||
|
||||
Reference in New Issue
Block a user