summaryrefslogtreecommitdiffstats
path: root/test-ipconfig
blob: 378c26902a244a246a13af3835328c826097bdd7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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()