1#!/usr/bin/python 2# Copyright (c) PLUMgrid, Inc. 3# Licensed under the Apache License, Version 2.0 (the "License") 4 5from sys import argv 6from bcc import BPF 7from builtins import input 8from ctypes import c_int, c_uint 9from http.server import HTTPServer, SimpleHTTPRequestHandler 10import json 11from netaddr import EUI, IPAddress 12from pyroute2 import IPRoute, NetNS, IPDB, NSPopen 13from socket import htons, AF_INET 14from threading import Thread 15from subprocess import call, Popen, PIPE 16 17num_hosts = int(argv[1]) 18host_id = int(argv[2]) 19dhcp = int(argv[3]) 20gretap = int(argv[4]) 21 22b = BPF(src_file="tunnel_mesh.c") 23ingress_fn = b.load_func("handle_ingress", BPF.SCHED_CLS) 24egress_fn = b.load_func("handle_egress", BPF.SCHED_CLS) 25tunkey2if = b.get_table("tunkey2if") 26if2tunkey = b.get_table("if2tunkey") 27conf = b.get_table("conf") 28 29ipr = IPRoute() 30ipdb = IPDB(nl=ipr) 31 32ifc = ipdb.interfaces.eth0 33 34# ifcs to cleanup at the end 35ifc_gc = [] 36 37# dhcp server and client processes 38d_serv = [] 39d_client = [] 40 41def run(): 42 if gretap: 43 with ipdb.create(ifname="gretap1", kind="gretap", gre_ikey=0, gre_okey=0, 44 gre_local='172.16.1.%d' % (100 + host_id), 45 gre_ttl=16, gre_collect_metadata=1) as vx: 46 vx.up() 47 ifc_gc.append(vx.ifname) 48 else: 49 with ipdb.create(ifname="vxlan0", kind="vxlan", vxlan_id=0, 50 vxlan_link=ifc, vxlan_port=4789, 51 vxlan_collect_metadata=True, 52 vxlan_learning=False) as vx: 53 vx.up() 54 ifc_gc.append(vx.ifname) 55 56 conf[c_int(1)] = c_int(vx.index) 57 58 ipr.tc("add", "ingress", vx.index, "ffff:") 59 ipr.tc("add-filter", "bpf", vx.index, ":1", fd=ingress_fn.fd, 60 name=ingress_fn.name, parent="ffff:", action="drop", classid=1) 61 62 for j in range(0, 2): 63 vni = 10000 + j 64 with ipdb.create(ifname="br%d" % j, kind="bridge") as br: 65 for i in range(0, num_hosts): 66 if i != host_id: 67 v = ipdb.create(ifname="dummy%d%d" % (j , i), kind="dummy").up().commit() 68 ipaddr = "172.16.1.%d" % (100 + i) 69 tunkey2if_key = tunkey2if.Key(vni) 70 tunkey2if_key.remote_ipv4 = IPAddress(ipaddr) 71 tunkey2if_leaf = tunkey2if.Leaf(v.index) 72 tunkey2if[tunkey2if_key] = tunkey2if_leaf 73 74 if2tunkey_key = if2tunkey.Key(v.index) 75 if2tunkey_leaf = if2tunkey.Leaf(vni) 76 if2tunkey_leaf.remote_ipv4 = IPAddress(ipaddr) 77 if2tunkey[if2tunkey_key] = if2tunkey_leaf 78 79 ipr.tc("add", "sfq", v.index, "1:") 80 ipr.tc("add-filter", "bpf", v.index, ":1", fd=egress_fn.fd, 81 name=egress_fn.name, parent="1:", action="drop", classid=1) 82 br.add_port(v) 83 br.up() 84 ifc_gc.append(v.ifname) 85 if dhcp == 0: 86 ipaddr = "99.1.%d.%d/24" % (j, host_id + 1) 87 br.add_ip(ipaddr) 88 ifc_gc.append(br.ifname) 89 90 # dhcp server only runs on host 0 91 if dhcp == 1 and host_id == 0: 92 for j in range(0, 2): 93 v1 = "dhcp%d_v1" % j 94 v2 = "dhcp%d_v2" % j 95 br = ipdb.interfaces["br%d" % j] 96 with ipdb.create(ifname=v1, kind="veth", peer=v2) as v: 97 v.up() 98 br.add_port(ipdb.interfaces[v1]).commit() 99 dhcp_v2 = ipdb.interfaces[v2] 100 dhcp_v2.add_ip("99.1.%d.1/24" % j).up().commit() 101 102 call(["/bin/rm", "-f", "/tmp/dnsmasq.%d.leases" % j]) 103 cmd = ["dnsmasq", "-d", "--bind-interfaces", "--strict-order", 104 "--conf-file=", 105 "--dhcp-range", "99.1.%d.2,99.1.%d.254,255.255.255.0,12h" % (j, j), 106 "--dhcp-no-override", "--except-interface=lo", 107 "--interface=dhcp%d_v2" % j, 108 "--dhcp-authoritative", 109 "--dhcp-leasefile=/tmp/dnsmasq.%d.leases" % j] 110 d_serv.append(Popen(cmd, stdout=PIPE, stderr=PIPE)) 111 112 # dhcp client to assign ip address for each bridge 113 if dhcp == 1: 114 for j in range(0, 2): 115 call(["/bin/rm", "-rf", "/tmp/dhcp_%d_%d" % (host_id, j)]) 116 call(["mkdir", "/tmp/dhcp_%d_%d" % (host_id, j)]) 117 call(["touch", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j)]) 118 call(["touch", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)]) 119 cmd = ["dhclient", "-d", "br%d" % j, 120 "-cf", "/tmp/dhcp_%d_%d/dhclient.conf" % (host_id, j), 121 "-lf", "/tmp/dhcp_%d_%d/dhclient.lease" % (host_id, j)] 122 d_client.append(Popen(cmd, stdout=PIPE, stderr=PIPE)) 123 124 # make sure we get address for eth0 125 retry = -1 126 while retry < 0: 127 check = Popen(["ip", "addr", "show", "br%d" % j], stdout=PIPE, stderr=PIPE) 128 out = check.stdout.read() 129 checkip = b"99.1.%d" % j 130 retry = out.find(checkip) 131 132try: 133 run() 134 input("") 135finally: 136 for v in ifc_gc: call(["ip", "link", "del", v]) 137 ipdb.release() 138 for p in d_client: p.kill() 139 for p in d_serv: p.kill() 140