diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2023-05-20 21:42:08 +0200 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2023-05-21 21:51:00 +0200 |
commit | b70cb5ddaeadc887c9645f61441b701392f10758 (patch) | |
tree | 4d1f0c7f410601bc6fb7be98515a81d93b81f522 | |
parent | 81c93ed662cd8b3f38871bc002582f5abee9b99f (diff) | |
download | klibc-maint-b70cb5ddaeadc887c9645f61441b701392f10758.tar.gz |
-rwxr-xr-x | test-ipconfig | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/test-ipconfig b/test-ipconfig new file mode 100755 index 0000000..378c269 --- /dev/null +++ b/test-ipconfig @@ -0,0 +1,120 @@ +#!/usr/bin/python3 + +import argparse +import ipaddress +import json +import subprocess +import sys +import tempfile + + +class NetNamespace: + def __init__(self, name): + self._name = name + + def __enter__(self): + subprocess.check_call(['ip', 'netns', 'add', self._name]) + + def __exit__(self, *_): + subprocess.run(['ip', 'netns', 'del', self._name]) + + +class VethNetwork: + def __init__(self, ipv4_network, ipv6_network, local_iface, + other_ns, other_iface): + self._ip_networks = [ipv4_network, ipv6_network] + self._local_iface = local_iface + self._other_ns = other_ns + self._other_iface = other_iface + + def __enter__(self): + subprocess.check_call( + ['ip', 'link', 'add', self._local_iface, 'type', 'veth', + 'peer', 'name', self._other_iface]) + try: + subprocess.check_call( + ['ip', 'link', 'set', self._local_iface, 'up']) + for ip_network in self._ip_networks: + subprocess.check_call( + ['ip', 'addr', 'add', 'dev', self._local_iface, + f'{ ip_network[1] }/{ ip_network.prefixlen }']) + subprocess.check_call( + ['ip', 'link', 'set', self._other_iface, + 'netns', self._other_ns]) + except: + subprocess.run(['ip', 'link', 'del', self._local_iface]) + raise + + def __exit__(self, *_): + subprocess.run(['ip', 'link', 'del', self._local_iface]) + + +class DnsmasqRunning: + def __init__(self, ipv4_network, ipv6_network, iface): + self._iface = iface + self._ipv4_network = ipv4_network + self._ipv6_network = ipv6_network + assert self._ipv6_network.prefixlen == 64 # for SLAAC + + def __enter__(self): + config = tempfile.NamedTemporaryFile(mode='w') + try: + config_text = f'''\ +interface={ self._iface } +bind-interfaces +enable-ra +dhcp-range={ self._ipv4_network[2] },{ self._ipv4_network[-2] } +synth-domain=dhcp.example.com,{ self._ipv4_network[0] }/{ self._ipv4_network.prefixlen } +dhcp-option=option6:nis-domain,nisexample +dhcp-option=option6:domain-search,a.example.com,b.example.com +dhcp-option=option6:bootfile-url,tftp://boot.example.com/example +dhcp-range={ self._ipv6_network[2] },{ self._ipv6_network[-2] },slaac,64 +synth-domain=dhcpv6.example.com,{ self._ipv6_network[0] }/{ self._ipv6_network.prefixlen } +''' + print('dnsmasq config:') + print(config_text) + config.write(config_text) + config.flush() + + self._dnsmasq_proc = subprocess.Popen( + ['dnsmasq', '-d', '-C', config.name, '--bootp-dynamic'], + stderr=sys.stderr) + self._config_file = config + except: + config.close() + raise + + def __exit__(self, *_): + self._dnsmasq_proc.terminate() + self._dnsmasq_proc.wait() + self._config_file.close() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('command', nargs='+') + parser.add_argument('--ipv4-network', default='192.168.234.0/24') + parser.add_argument('--ipv6-network', default='fc00:c001:d00d:cafe::/64') + parser.add_argument('--server-iface', default='veth-dnsmasq') + parser.add_argument('--net-namespace', default='test-ipconfig') + parser.add_argument('--client-iface', default='veth-ipconfig') + args = parser.parse_args() + + ipv4_network = ipaddress.IPv4Network(args.ipv4_network) + ipv6_network = ipaddress.IPv6Network(args.ipv6_network) + with NetNamespace(args.net_namespace), \ + VethNetwork(ipv4_network, ipv6_network, args.server_iface, + args.net_namespace, args.client_iface), \ + DnsmasqRunning(ipv4_network, ipv6_network, args.server_iface): + subprocess.check_call( + ['ip', 'netns', 'exec', args.net_namespace] + args.command) + net_conf_name = f'/run/net-{ args.client_iface }.conf' + print(f'{net_conf_name}:') + subprocess.run(['cat', net_conf_name]) + print(f'ip addr:') + subprocess.check_call( + ['ip', 'netns', 'exec', args.net_namespace, 'ip', 'addr']) + + +if __name__ == '__main__': + main() |