pax_global_header 0000666 0000000 0000000 00000000064 14272470301 0014512 g ustar 00root root 0000000 0000000 52 comment=ea9f97f82687b87b3c891babaf5eb3f51c6f78c5 re6stnet-ea9f97f82687b87b3c891babaf5eb3f51c6f78c5-demo/ 0000775 0000000 0000000 00000000000 14272470301 0021471 5 ustar 00root root 0000000 0000000 re6stnet-ea9f97f82687b87b3c891babaf5eb3f51c6f78c5-demo/demo/ 0000775 0000000 0000000 00000000000 14272470301 0022415 5 ustar 00root root 0000000 0000000 re6stnet-ea9f97f82687b87b3c891babaf5eb3f51c6f78c5-demo/demo/demo 0000775 0000000 0000000 00000051511 14272470301 0023272 0 ustar 00root root 0000000 0000000 #!/usr/bin/python2 import argparse, math, nemu, os, re, signal import socket, sqlite3, subprocess, sys, time, weakref from collections import defaultdict from contextlib import contextmanager from threading import Thread IPTABLES = 'iptables' SCREEN = 'screen' VERBOSE = 4 REGISTRY = '10.0.0.2' REGISTRY2 = '10.3.0.2' REGISTRY_SERIAL = '0x120010db80042' REGISTRY2_SERIAL = '0x120010db80043' CA_DAYS = 1000 # Quick check to avoid wasting time if there is an error. with open(os.devnull, "wb") as f: for x in 're6stnet', 're6st-conf', 're6st-registry': subprocess.check_call(('./py', x, '--help'), stdout=f) # # Underlying network: # # registry .2------ ------.2 registry2 # | | # 10.0.0| |10.3.0 # .1 | |.1 # ---------------Internet---------------- # |.1 |.1 |.1 # |10.1.0 |10.2.0 | # |.2 |.2 | # gateway1 gateway2 s3:10.0.1 # |.1 |.1 |.2 |.3 |.4 # s1:10.1.1 --s2:10.2.1-- m6 m7 m8 # |.2 |.3 |.2 |.3 |.4 |.5 | # m1 m2 m3 m4 m5 m10 m9 # # Overlay re6st network: # 2001:db8::1 # registry--------internet-------registry2 # |::1 ::1| # |2001:db8:42 2001:db8:43| # :1:: :3:: |:5:: :7:: :1::1| # m1-m2-m3-m4-m5-m6-m7-m8 m10 # :2:: :4:: :6:: :8:: def disable_signal_on_children(sig): pid = os.getpid() sigint = signal.signal(sig, lambda *x: os.getpid() == pid and sigint(*x)) disable_signal_on_children(signal.SIGINT) Node__add_interface = nemu.Node._add_interface def _add_interface(node, iface): iface.__dict__['node'] = weakref.proxy(node) return Node__add_interface(node, iface) nemu.Node._add_interface = _add_interface parser = argparse.ArgumentParser() parser.add_argument('port', type = int, help = 'port used to display tunnels') parser.add_argument('-d', '--duration', type = int, help = 'time of the demo execution in seconds') parser.add_argument('-p', '--ping', action = 'store_true', help = 'execute ping utility') parser.add_argument('-m', '--hmac', action = 'store_true', help = 'execute HMAC test') args = parser.parse_args() def handler(signum, frame): sys.exit() if args.duration: signal.signal(signal.SIGALRM, handler) signal.alarm(args.duration) execfile("fixnemu.py") # create nodes for name in """internet=I registry=R gateway1=g1 machine1=1 machine2=2 gateway2=g2 machine3=3 machine4=4 machine5=5 machine6=6 machine7=7 machine8=8 machine9=9 registry2=R2 machine10=10 """.split(): name, short = name.split('=') globals()[name] = node = nemu.Node() node.name = name node.short = short node.Popen(('sysctl', '-q', 'net.ipv4.icmp_echo_ignore_broadcasts=0')).wait() node._screen = node.Popen((SCREEN, '-DmS', name)) node.screen = (lambda name: lambda *cmd: subprocess.call([SCREEN, '-r', name, '-X', 'eval'] + map( """screen sh -c 'set %s; "\$@"; echo "\$@"; exec $SHELL'""" .__mod__, cmd)))(name) # create switch switch1 = nemu.Switch() switch2 = nemu.Switch() switch3 = nemu.Switch() #create interfaces re_if_0, in_if_0 = nemu.P2PInterface.create_pair(registry, internet) in_if_1, g1_if_0 = nemu.P2PInterface.create_pair(internet, gateway1) in_if_2, g2_if_0 = nemu.P2PInterface.create_pair(internet, gateway2) m6_if_1, m9_if_0 = nemu.P2PInterface.create_pair(machine6, machine9) r2_if_0, in_if_4 = nemu.P2PInterface.create_pair(registry2, internet) g1_if_0_name = g1_if_0.name gateway1.Popen((IPTABLES, '-t', 'nat', '-A', 'POSTROUTING', '-o', g1_if_0_name, '-j', 'MASQUERADE')).wait() gateway1.Popen((IPTABLES, '-t', 'nat', '-N', 'MINIUPNPD')).wait() gateway1.Popen((IPTABLES, '-t', 'nat', '-A', 'PREROUTING', '-i', g1_if_0_name, '-j', 'MINIUPNPD')).wait() gateway1.Popen((IPTABLES, '-N', 'MINIUPNPD')).wait() machine9.Popen(('sysctl', 'net.ipv6.conf.%s.accept_ra=2' % m9_if_0.name)).wait() # Enable forwarding for communication between registry and registry2 internet.Popen(('sysctl', '-q', 'net.ipv6.conf.all.forwarding=1')).wait() in_if_3 = nemu.NodeInterface(internet) g1_if_1 = nemu.NodeInterface(gateway1) g2_if_1 = nemu.NodeInterface(gateway2) m1_if_0 = nemu.NodeInterface(machine1) m2_if_0 = nemu.NodeInterface(machine2) m3_if_0 = nemu.NodeInterface(machine3) m4_if_0 = nemu.NodeInterface(machine4) m5_if_0 = nemu.NodeInterface(machine5) m6_if_0 = nemu.NodeInterface(machine6) m7_if_0 = nemu.NodeInterface(machine7) m8_if_0 = nemu.NodeInterface(machine8) m10_if_0 = nemu.NodeInterface(machine10) # connect to switch switch1.connect(g1_if_1) switch1.connect(m1_if_0) switch1.connect(m2_if_0) switch2.connect(g2_if_1) switch2.connect(m3_if_0) switch2.connect(m4_if_0) switch2.connect(m5_if_0) switch2.connect(m10_if_0) switch3.connect(in_if_3) switch3.connect(m6_if_0) switch3.connect(m7_if_0) switch3.connect(m8_if_0) # setting everything up switch1.up = switch2.up = switch3.up = True re_if_0.up = in_if_0.up = in_if_1.up = g1_if_0.up = in_if_2.up = g2_if_0.up = True in_if_3.up = g1_if_1.up = g2_if_1.up = m1_if_0.up = m2_if_0.up = m3_if_0.up = True m4_if_0.up = m5_if_0.up = m6_if_0.up = m6_if_1.up = m7_if_0.up = m8_if_0.up = True m9_if_0.up = m10_if_0.up = in_if_4.up = r2_if_0.up = True # Add IPv4 addresses re_if_0.add_v4_address(address=REGISTRY, prefix_len=24) r2_if_0.add_v4_address(address=REGISTRY2, prefix_len=24) in_if_0.add_v4_address(address='10.0.0.1', prefix_len=24) in_if_1.add_v4_address(address='10.1.0.1', prefix_len=24) in_if_2.add_v4_address(address='10.2.0.1', prefix_len=24) in_if_3.add_v4_address(address='10.0.1.1', prefix_len=24) in_if_3.add_v6_address(address='2001:db8::1', prefix_len=48) g1_if_0.add_v4_address(address='10.1.0.2', prefix_len=24) g1_if_1.add_v4_address(address='10.1.1.1', prefix_len=24) g2_if_0.add_v4_address(address='10.2.0.2', prefix_len=24) g2_if_1.add_v4_address(address='10.2.1.1', prefix_len=24) m1_if_0.add_v4_address(address='10.1.1.2', prefix_len=24) m2_if_0.add_v4_address(address='10.1.1.3', prefix_len=24) m3_if_0.add_v4_address(address='10.2.1.2', prefix_len=24) m4_if_0.add_v4_address(address='10.2.1.3', prefix_len=24) m5_if_0.add_v4_address(address='10.2.1.4', prefix_len=24) m10_if_0.add_v4_address(address='10.2.1.5', prefix_len=24) m6_if_0.add_v4_address(address='10.0.1.2', prefix_len=24) m7_if_0.add_v4_address(address='10.0.1.3', prefix_len=24) m8_if_0.add_v4_address(address='10.0.1.4', prefix_len=24) m6_if_1.add_v4_address(address='192.168.241.1', prefix_len=24) in_if_4.add_v4_address(address='10.3.0.1', prefix_len=24) # Add IPv6 addresses to test UDP6 between m8 and m6/m7 m6_if_0.add_v6_address(address='fc42:6::1', prefix_len=16) m7_if_0.add_v6_address(address='fc42:7::1', prefix_len=16) m8_if_0.add_v6_address(address='fc42:8::1', prefix_len=16) def add_llrtr(iface, peer, dst='default'): for a in peer.get_addresses(): a = a['address'] if a.startswith('fe80:'): return iface.node.Popen(('ip', 'route', 'add', dst, 'via', a, 'proto', 'static', 'dev', iface.name)).wait() # setup routes add_llrtr(re_if_0, in_if_0) add_llrtr(r2_if_0, in_if_4) add_llrtr(in_if_0, re_if_0, '2001:db8:42::/48') add_llrtr(in_if_4, r2_if_0, '2001:db8:43::/48') registry.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.0.0.1') registry2.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.3.0.1') internet.add_route(prefix='10.2.0.0', prefix_len=16, nexthop='10.2.0.2') gateway1.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.1.0.1') gateway2.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.2.0.1') for m in machine1, machine2: m.add_route(nexthop='10.1.1.1') for m in machine3, machine4, machine5, machine10: m.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.2.1.1') for m in machine6, machine7, machine8: m.add_route(prefix='10.0.0.0', prefix_len=8, nexthop='10.0.1.1') # Test connectivity first. Run process, hide output and check # return code null = file(os.devnull, "r+") for ip in '10.1.1.2', '10.1.1.3', '10.2.1.2', '10.2.1.3': if machine1.Popen(('ping', '-c1', ip), stdout=null).wait(): print 'Failed to ping %s' % ip break else: print "Connectivity IPv4 OK!" nodes = [] gateway1.screen('miniupnpd -d -f miniupnpd.conf -P miniupnpd.pid' ' -a %s -i %s' % (g1_if_1.name, g1_if_0_name)) @contextmanager def new_network(registry, reg_addr, serial, ca): from OpenSSL import crypto import hashlib, sqlite3 os.path.exists(ca) or subprocess.check_call( "openssl req -nodes -new -x509 -key %s/ca.key -out %s" " -subj /CN=re6st.example.com/emailAddress=re6st@example.com" " -set_serial %s -days %u" % (registry.name, ca, serial, CA_DAYS), shell=True) with open(ca) as f: cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read()) fingerprint = "sha256:" + hashlib.sha256( crypto.dump_certificate(crypto.FILETYPE_ASN1, cert)).hexdigest() db_path = "%s/registry.db" % registry.name registry.screen("./py re6st-registry @%s/re6st-registry.conf" " --db %s --mailhost %s -v%u" % (registry.name, db_path, os.path.abspath('mbox'), VERBOSE)) registry_url = 'http://%s/' % reg_addr registry.Popen(('python', '-c', """if 1: import socket, time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) while True: try: s.connect(('localhost', 80)) break except socket.error: time.sleep(.1) """)).wait() db = sqlite3.connect(db_path, isolation_level=None) def new_node(node, folder, args='', prefix_len=None, registry=registry_url): nodes.append(node) if not os.path.exists(folder + '/cert.crt'): dh_path = folder + '/dh2048.pem' if not os.path.exists(dh_path): os.symlink('../dh2048.pem', dh_path) email = node.name + '@example.com' p = node.Popen(('../py', 're6st-conf', '--registry', registry, '--email', email, '--fingerprint', fingerprint), stdin=subprocess.PIPE, cwd=folder) token = None while not token: time.sleep(.1) token = db.execute("SELECT token FROM token WHERE email=?", (email,)).fetchone() if prefix_len: db.execute("UPDATE token SET prefix_len=? WHERE token=?", (prefix_len, token[0])) p.communicate(str(token[0])) os.remove(dh_path) os.remove(folder + '/ca.crt') node.re6st_cmdline = ( './py re6stnet @%s/re6stnet.conf -v%u --registry %s' ' --console %s/run/console.sock %s' ) % (folder, VERBOSE, registry, folder, args) node.screen(node.re6st_cmdline) new_node(registry, registry.name, '--ip ' + reg_addr, registry='http://localhost/') yield new_node db.close() with new_network(registry, REGISTRY, REGISTRY_SERIAL, 'ca.crt') as new_node: new_node(machine1, 'm1', '-I%s' % m1_if_0.name) new_node(machine2, 'm2', '--remote-gateway 10.1.1.1', prefix_len=77) new_node(machine3, 'm3', '-i%s' % m3_if_0.name) new_node(machine4, 'm4', '-i%s' % m4_if_0.name) new_node(machine5, 'm5', '-i%s' % m5_if_0.name) new_node(machine6, 'm6', '-I%s' % m6_if_1.name) new_node(machine7, 'm7') new_node(machine8, 'm8') with new_network(registry2, REGISTRY2, REGISTRY2_SERIAL, 'ca2.crt') as new_node: new_node(machine10, 'm10', '-i%s' % m10_if_0.name) if args.ping: for j, machine in enumerate(nodes): ips = [ '2001:db8:42::1' if i == 0 else '2001:db8:42:2::' if i == 2 else '2001:db8:43::1' if i == 9 else '2001:db8:43:1::1' if i == 10 else # Only 1 address for machine2 because prefix_len = 80,+48 = 128 '2001:db8:42:%s::1' % i for i in xrange(11) if i != j] name = machine.name if machine.short[0] == 'R' else 'm' + machine.short machine.screen('python ping.py {} {}'.format(name, ' '.join(ips))) class testHMAC(Thread): def run(self): updateHMAC = ('python', '-c', "import urllib, sys; sys.exit(" "204 != urllib.urlopen('http://127.0.0.1/updateHMAC').code)") reg1_db = sqlite3.connect('registry/registry.db', isolation_level=None, check_same_thread=False) reg2_db = sqlite3.connect('registry2/registry.db', isolation_level=None, check_same_thread=False) reg1_db.text_factory = reg2_db.text_factory = str m_net1 = 'registry', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8' m_net2 = 'registry2', 'm10' print 'Testing HMAC, letting the time to machines to create tunnels...' time.sleep(45) print 'Check that the initial HMAC config is deployed on network 1' test_hmac.checkHMAC(reg1_db, m_net1) print 'Test that a HMAC update works with nodes that are up' registry.backticks_raise(updateHMAC) print 'Updated HMAC (config = hmac0 & hmac1), waiting...' time.sleep(60) print 'Checking HMAC on machines connected to registry 1...' test_hmac.checkHMAC(reg1_db, m_net1) print ('Test that machines can update upon reboot ' + 'when they were off during a HMAC update.') test_hmac.killRe6st(machine1) print 'Re6st on machine 1 is stopped' time.sleep(5) registry.backticks_raise(updateHMAC) print 'Updated HMAC on registry (config = hmac1 & hmac2), waiting...' time.sleep(60) machine1.screen(machine1.re6st_cmdline) print 'Started re6st on machine 1, waiting for it to get new conf' time.sleep(60) print 'Checking HMAC on machines connected to registry 1...' test_hmac.checkHMAC(reg1_db, m_net1) print 'Testing of HMAC done!' # TODO: missing last step reg1_db.close() reg2_db.close() if args.hmac: import test_hmac t = testHMAC() t.deamon = 1 t.start() del t _ll = {} def node_by_ll(addr): try: return _ll[addr] except KeyError: for n in nodes: for i in n.get_interfaces(): t = isinstance(i, nemu.interface.ImportedNodeInterface) try: a = i.get_addresses() except KeyError: break for a in a: p = a['prefix_len'] a = a['address'] if a.startswith('10.'): if a.startswith('10.42.'): assert not p % 8 _ll[socket.inet_ntoa(socket.inet_aton( a)[:p/8].ljust(4, '\0'))] = n, t elif a.startswith('2001:db8:'): assert not p % 8 a = socket.inet_ntop(socket.AF_INET6, socket.inet_pton(socket.AF_INET6, a)[:p/8].ljust(16, '\0')) elif not a.startswith('fe80::'): continue _ll[a] = n, t return _ll[addr] def route_svg(ipv4, z = 4, default = type('', (), {'short': None})): graph = {} for n in nodes: g = graph[n] = defaultdict(list) for r in n.get_routes(): if (r.prefix and r.prefix.startswith('10.42.') if ipv4 else r.prefix is None or r.prefix.startswith('2001:db8:')): try: g[node_by_ll(r.nexthop)].append( node_by_ll(r.prefix)[0] if r.prefix else default) except KeyError: pass gv = ["digraph { splines = true; edge[color=grey, labelangle=0];"] N = len(nodes) a = 2 * math.pi / N edges = set() for i, n in enumerate(nodes): gv.append('%s[pos="%s,%s!"];' % (n.name, z * math.cos(a * i), z * math.sin(a * i))) l = [] for p, r in graph[n].iteritems(): j = abs(nodes.index(p[0]) - i) l.append((min(j, N - j), p, r)) for j, (l, (p, t), r) in enumerate(sorted(l)): l = [] arrowhead = 'none' for r in sorted(r.short for r in r): if r: if r == p.short: r = '%s' % r l.append(r) else: arrowhead = 'dot' if (n.name, p.name) in edges: r = 'penwidth=0' else: edges.add((p.name, n.name)) r = 'style=solid' if t else 'style=dashed' gv.append( '%s -> %s [labeldistance=%u, headlabel=<%s>, arrowhead=%s, %s];' % (p.name, n.name, 1.5 * math.sqrt(j) + 2, ','.join(l), arrowhead, r)) gv.append('}\n') return subprocess.Popen(('neato', '-Tsvg'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, ).communicate('\n'.join(gv))[0] if args.port: import SimpleHTTPServer, SocketServer class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): _path_match = re.compile('/(.+)\.(html|svg)$').match pages = 'ipv6', 'ipv4', 'tunnels' def do_GET(self): svg = None try: name, ext = self._path_match(self.path).groups() page = self.pages.index(name) except AttributeError, ValueError: if self.path == '/': self.send_response(302) self.send_header('Location', self.pages[0] + '.html') self.end_headers() else: self.send_error(404) return if page < 2: body = route_svg(page) else: body = registry.Popen(('python', '-c', r"""if 1: import math, json from re6st.registry import RegistryClient g = json.loads(RegistryClient( 'http://localhost/').topology()) r = set(g.pop('', ())) a = set() for v in g.itervalues(): a.update(v) g.update(dict.fromkeys(a.difference(g), ())) print 'digraph {' a = 2 * math.pi / len(g) z = 4 m2 = '%u/80' % (2 << 64) title = lambda n: '2|80' if n == m2 else n g = sorted((title(k), k in r, v) for k, v in g.iteritems()) for i, (n, r, v) in enumerate(g): print '"%s"[pos="%s,%s!"%s];' % (title(n), z * math.cos(a * i), z * math.sin(a * i), '' if r else ', style=dashed') for v in v: print '"%s" -> "%s";' % (n, title(v)) print '}' """), stdout=subprocess.PIPE, cwd="..").communicate()[0] if body: body = subprocess.Popen(('neato', '-Tsvg'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, ).communicate(body)[0] if not body: self.send_error(500) return if ext == 'svg': mt = 'image/svg+xml' else: mt = 'text/html' body = """
%s %s """ % (name, ' '.join(x if i == page else '%s' % (x, x) for i, x in enumerate(self.pages)), body[body.find('