Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
99 changes: 17 additions & 82 deletions test/infamy/dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
import os
import tempfile as tf
import subprocess
from .netns import NetnsService


class Server:
class Server(NetnsService):
config_file = '/tmp/udhcpd.conf'
leases_file = '/tmp/udhcpd.leases'

def __init__(self, netns, start='192.168.0.100', end='192.168.0.110',
netmask='255.255.255.0', ip=None, router=None, prefix=None,
hostname=None, iface="iface"):
self.process = None
self.netns = netns
super().__init__(netns)
self.iface = iface
self._create_files(start, end, netmask, ip, router, prefix, hostname)

Expand All @@ -26,12 +26,6 @@ def __del__(self):
except:
pass

def __enter__(self):
self.start()

def __exit__(self, _, __, ___):
self.stop()

def _create_files(self, start, end, netmask, ip, router, prefix, hostname):
f = open(self.leases_file, "w")
f.close()
Expand All @@ -56,20 +50,8 @@ def _create_files(self, start, end, netmask, ip, router, prefix, hostname):
if hostname:
f.write(f"option hostname {hostname}\n")

def get_pid(self):
return self.process.pid

def start(self):
if not os.path.exists(self.config_file):
raise Exception("Config file does not exist. Please create it first.")
cmd = f"udhcpd -f {self.config_file}"
self.process = self.netns.popen(cmd.split(" "))

def stop(self):
if self.process:
self.process.terminate()
self.process.wait()
self.process = None
def argv(self):
return ["udhcpd", "-f", self.config_file]


class Client:
Expand Down Expand Up @@ -101,8 +83,10 @@ def lease(self):
return tuple(res.stdout.split())


class Server6Dnsmasq:
class Server6Dnsmasq(NetnsService):
"""DHCPv6 server using dnsmasq"""
# Drop the DEVNULL redirect to debug
popen_kwargs = dict(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def __init__(self, netns, start=None, end=None, dns=None, domain=None,
iface="iface", address=None):
Expand All @@ -116,8 +100,7 @@ def __init__(self, netns, start=None, end=None, dns=None, domain=None,
domain: DNS search domain
iface: Interface to listen on
"""
self.process = None
self.netns = netns
super().__init__(netns)
self.iface = iface
self.config_file = tf.NamedTemporaryFile(mode='w', prefix='dnsmasq6_',
suffix='.conf', delete=False)
Expand All @@ -143,13 +126,6 @@ def __del__(self):
except:
pass

def __enter__(self):
self.start()
return self

def __exit__(self, _, __, ___):
self.stop()

def _create_config(self, start, end, dns, domain, address):
"""Create dnsmasq configuration for DHCPv6"""
with self.hosts_file:
Expand Down Expand Up @@ -191,26 +167,12 @@ def _create_config(self, start, end, dns, domain, address):
self.config_file.write("log-debug\n")
self.config_file.write("log-dhcp\n")

def start(self):
"""Start the DHCPv6 server"""
if not os.path.exists(self.config_path):
raise Exception("Config file does not exist")

# Drop DEVNULL redirec to debug
cmd = f"dnsmasq --conf-file={self.config_path} --no-daemon"
self.process = self.netns.popen(cmd.split(" "),
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

def stop(self):
"""Stop the DHCPv6 server"""
if self.process:
self.process.terminate()
self.process.wait()
self.process = None

class Server6Dhcpd:
def argv(self):
return ["dnsmasq", f"--conf-file={self.config_path}", "--no-daemon"]

class Server6Dhcpd(NetnsService):
"""DHCPv6 server using ISC dhcpd with prefix delegation support"""
popen_kwargs = dict(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def __init__(self, netns, start=None, end=None, prefix=None, prefix_len=64,
dns=None, domain=None, iface="iface", address=None, subnet="2001:db8::/48"):
Expand All @@ -228,8 +190,7 @@ def __init__(self, netns, start=None, end=None, prefix=None, prefix_len=64,
address: Server address on the interface (for subnet config)
subnet: Subnet declaration (e.g., "2001:db8::/48")
"""
self.process = None
self.netns = netns
super().__init__(netns)
self.iface = iface
self.subnet = subnet

Expand Down Expand Up @@ -260,13 +221,6 @@ def __del__(self):
except:
pass

def __enter__(self):
self.start()
return self

def __exit__(self, _, __, ___):
self.stop()

def _create_config(self, start, end, prefix, prefix_len, dns, domain):
"""Create ISC dhcpd configuration for DHCPv6 with prefix delegation"""
with self.config_file:
Expand Down Expand Up @@ -331,11 +285,7 @@ def _create_config(self, start, end, prefix, prefix_len, dns, domain):

self.config_file.write("}\n")

def start(self):
"""Start the DHCPv6 server"""
if not os.path.exists(self.config_path):
raise Exception("Config file does not exist")

def argv(self):
# Debug: show config and interface status
# self.netns.popen(f"cat {self.config_path}".split(" "))
# self.netns.popen("ifconfig")
Expand All @@ -347,7 +297,7 @@ def start(self):
# -cf: Config file
# -lf: Lease file
# -pf: PID file
cmd = [
return [
"dhcpd",
"-6", # IPv6 mode
"-f", # Foreground
Expand All @@ -357,18 +307,3 @@ def start(self):
"-pf", self.pid_path,
self.iface # Interface to listen on
]

self.process = self.netns.popen(cmd,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)

def stop(self):
"""Stop the DHCPv6 server"""
if self.process:
self.process.terminate()
try:
self.process.wait(timeout=5)
except subprocess.TimeoutExpired:
self.process.kill()
self.process.wait()
self.process = None
34 changes: 7 additions & 27 deletions test/infamy/file_server.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,26 @@
"""
Basic file server over HTTP
"""
import concurrent.futures
import functools
import http.server
import socket
from infamy.util import until
from .netns import NetnsService

class FileServer:
class FileServer(NetnsService):
"""Open web server on (address, port) serving files from directory"""
def __init__(self, netns, address, port, directory):
super().__init__(netns)
self.address = address
self.port = port
self.directory = directory
self.netns = netns
self.process = None
self.check_addres = None

def argv(self):
return f"httpd -p {self.address}:{self.port} -f -h {self.directory}".split(" ")

def start(self):
"""start HTTP file server"""
cmd = f"httpd -p {self.address}:{self.port} -f -h {self.directory}"
self.process = self.netns.popen(cmd.split(" "))
def ready(self):
if self.address == "[::]":
check_address = "::1"
elif self.address == "0.0.0.0":
check_address = "127.0.0.1"
else:
check_address=self.address
check_address = self.address
cmd = f"nc -z {check_address} {self.port}".split()
until(lambda: self.netns.run(cmd).returncode == 0)


def stop(self):
"""Stop HTTP file server"""
if self.process:
self.process.terminate()
self.process.wait()
self.process = None

def __enter__(self):
self.start()

def __exit__(self, _, __, ___):
self.stop()
32 changes: 19 additions & 13 deletions test/infamy/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,28 @@
import subprocess
import time
from typing import Tuple, List
from .netns import NetnsService
from .sniffer import Sniffer
from .portscanner import PortScanner
from .util import until


class DnatTarget(NetnsService):
"""One-shot nc server that answers DNAT-TEST-OK on port"""
popen_kwargs = dict(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

def __init__(self, netns, port):
super().__init__(netns)
self.port = port

def argv(self):
return ["nc", "-l", "-p", str(self.port), "-e", "/bin/echo", "DNAT-TEST-OK"]

def ready(self):
# Probing the port would use up the only connection nc accepts
time.sleep(1)


class Firewall:
"""Specialized utilities for testing firewall functionality"""

Expand Down Expand Up @@ -203,21 +220,10 @@ def verify_dnat(self, gateway_ip: str, forward_port: int, target_port: int,
Tuple of (dnat_working: bool, details: str)
"""
try:
# Use netcat to simulate a simple service on target port
cmd = f"nc -l -p {target_port} -e /bin/echo 'DNAT-TEST-OK'"
pid = self.dstns.popen(cmd.split(), stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
time.sleep(1) # Give server time to start

# Test connection from source to gateway:forward_port
cmd = f"nc -w {timeout} {gateway_ip} {forward_port}"
result = self.srcns.runsh(cmd)

try:
pid.terminate()
pid.wait(timeout=1)
except:
pid.kill()
with DnatTarget(self.dstns, target_port):
result = self.srcns.runsh(cmd)

# Check if we got the expected response
if "DNAT-TEST-OK" in result.stdout:
Expand Down
88 changes: 18 additions & 70 deletions test/infamy/multicast.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,31 @@
import time
import subprocess
import signal
import sys
from scapy.all import Ether, sendp

class MCastSender:
def __init__(self, netns,group):
self.group = group
self.netns = netns

def __enter__(self):
cmd = f"msend -I iface -g {self.group}"
arg = cmd.split(" ")

self.proc = self.netns.popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def __exit__(self, _, __, ___):
if not self.proc:
return False
import subprocess
from .netns import NetnsService

sys.stdout.flush()
self.proc.send_signal(signal.SIGINT)
time.sleep(1)
if not self.proc.poll():
try:
self.proc.kill()
except OSError:
pass
self.proc.wait()
class _MCastService(NetnsService):
"""msend, mreceive and the scapy sender all exit on SIGINT"""
stop_signal = signal.SIGINT
# Nothing reads the output. A pipe would fill up and block the
# process, so discard it.
popen_kwargs = dict(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

class MCastReceiver:
def __init__(self, netns, group):
super().__init__(netns)
self.group = group
self.netns = netns

def __enter__(self):
cmd = f"mreceive -I iface -g {self.group}"
arg = cmd.split(" ")

self.proc = self.netns.popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def __exit__(self, _, __, ___):
if not self.proc:
return False
class MCastSender(_MCastService):
def argv(self):
return f"msend -I iface -g {self.group}".split(" ")

sys.stdout.flush()
self.proc.send_signal(signal.SIGINT)
time.sleep(3)
if not self.proc.poll():
print("PROC")
try:
self.proc.kill()
except OSError:
print("ERR")
pass
self.proc.wait()

class MacMCastSender:
def __init__(self, netns, group):
self.group = group
self.netns = netns
class MCastReceiver(_MCastService):
def argv(self):
return f"mreceive -I iface -g {self.group}".split(" ")

def __enter__(self):
class MacMCastSender(_MCastService):
def argv(self):
send_cmd = (
"from scapy.all import sendp, Ether; "
f"pkt=Ether(src='aa:bb:cc:dd:ee:ff', dst='{self.group}', type=0xdead); "
"sendp(pkt, iface='iface', loop=1, inter=1./10)"
)
self.proc = self.netns.popen(["python3", "-c", send_cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return self

def __exit__(self, _, __, ___):
if self.proc:
sys.stdout.flush()
self.proc.send_signal(signal.SIGINT)
time.sleep(1)
if not self.proc.poll():
try:
self.proc.kill()
except OSError:
pass
self.proc.wait()
return ["python3", "-c", send_cmd]
Loading
Loading