1*387f9dfdSAndroid Build Coastguard Workerimport os 2*387f9dfdSAndroid Build Coastguard Workerimport subprocess 3*387f9dfdSAndroid Build Coastguard Workerimport pyroute2 4*387f9dfdSAndroid Build Coastguard Workerfrom pyroute2 import IPRoute, NetNS, IPDB, NSPopen 5*387f9dfdSAndroid Build Coastguard Worker 6*387f9dfdSAndroid Build Coastguard Workerclass Simulation(object): 7*387f9dfdSAndroid Build Coastguard Worker """ 8*387f9dfdSAndroid Build Coastguard Worker Helper class for controlling multiple namespaces. Inherit from 9*387f9dfdSAndroid Build Coastguard Worker this class and setup your namespaces. 10*387f9dfdSAndroid Build Coastguard Worker """ 11*387f9dfdSAndroid Build Coastguard Worker 12*387f9dfdSAndroid Build Coastguard Worker def __init__(self, ipdb): 13*387f9dfdSAndroid Build Coastguard Worker self.ipdb = ipdb 14*387f9dfdSAndroid Build Coastguard Worker self.ipdbs = {} 15*387f9dfdSAndroid Build Coastguard Worker self.namespaces = [] 16*387f9dfdSAndroid Build Coastguard Worker self.processes = [] 17*387f9dfdSAndroid Build Coastguard Worker self.released = False 18*387f9dfdSAndroid Build Coastguard Worker 19*387f9dfdSAndroid Build Coastguard Worker # helper function to add additional ifc to namespace 20*387f9dfdSAndroid Build Coastguard Worker # if called directly outside Simulation class, "ifc_base_name" should be 21*387f9dfdSAndroid Build Coastguard Worker # different from "name", the "ifc_base_name" and "name" are the same for 22*387f9dfdSAndroid Build Coastguard Worker # the first ifc created by namespace 23*387f9dfdSAndroid Build Coastguard Worker def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, 24*387f9dfdSAndroid Build Coastguard Worker out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, 25*387f9dfdSAndroid Build Coastguard Worker action="ok", disable_ipv6=False): 26*387f9dfdSAndroid Build Coastguard Worker if name in self.ipdbs: 27*387f9dfdSAndroid Build Coastguard Worker ns_ipdb = self.ipdbs[name] 28*387f9dfdSAndroid Build Coastguard Worker else: 29*387f9dfdSAndroid Build Coastguard Worker try: 30*387f9dfdSAndroid Build Coastguard Worker nl=NetNS(name) 31*387f9dfdSAndroid Build Coastguard Worker self.namespaces.append(nl) 32*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 33*387f9dfdSAndroid Build Coastguard Worker # remove the namespace if it has been created 34*387f9dfdSAndroid Build Coastguard Worker pyroute2.netns.remove(name) 35*387f9dfdSAndroid Build Coastguard Worker raise 36*387f9dfdSAndroid Build Coastguard Worker ns_ipdb = IPDB(nl) 37*387f9dfdSAndroid Build Coastguard Worker self.ipdbs[nl.netns] = ns_ipdb 38*387f9dfdSAndroid Build Coastguard Worker if disable_ipv6: 39*387f9dfdSAndroid Build Coastguard Worker cmd1 = ["sysctl", "-q", "-w", 40*387f9dfdSAndroid Build Coastguard Worker "net.ipv6.conf.default.disable_ipv6=1"] 41*387f9dfdSAndroid Build Coastguard Worker nsp = NSPopen(ns_ipdb.nl.netns, cmd1) 42*387f9dfdSAndroid Build Coastguard Worker nsp.wait(); nsp.release() 43*387f9dfdSAndroid Build Coastguard Worker try: 44*387f9dfdSAndroid Build Coastguard Worker ns_ipdb.interfaces.lo.up().commit() 45*387f9dfdSAndroid Build Coastguard Worker except pyroute2.ipdb.exceptions.CommitException: 46*387f9dfdSAndroid Build Coastguard Worker print("Warning, commit for lo failed, operstate may be unknown") 47*387f9dfdSAndroid Build Coastguard Worker if in_ifc: 48*387f9dfdSAndroid Build Coastguard Worker in_ifname = in_ifc.ifname 49*387f9dfdSAndroid Build Coastguard Worker with in_ifc as v: 50*387f9dfdSAndroid Build Coastguard Worker # move half of veth into namespace 51*387f9dfdSAndroid Build Coastguard Worker v.net_ns_fd = ns_ipdb.nl.netns 52*387f9dfdSAndroid Build Coastguard Worker else: 53*387f9dfdSAndroid Build Coastguard Worker # delete the potentially leaf-over veth interfaces 54*387f9dfdSAndroid Build Coastguard Worker ipr = IPRoute() 55*387f9dfdSAndroid Build Coastguard Worker for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link("del", index=i) 56*387f9dfdSAndroid Build Coastguard Worker ipr.close() 57*387f9dfdSAndroid Build Coastguard Worker try: 58*387f9dfdSAndroid Build Coastguard Worker out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", 59*387f9dfdSAndroid Build Coastguard Worker peer="%sb" % ifc_base_name).commit() 60*387f9dfdSAndroid Build Coastguard Worker in_ifc = self.ipdb.interfaces[out_ifc.peer] 61*387f9dfdSAndroid Build Coastguard Worker in_ifname = in_ifc.ifname 62*387f9dfdSAndroid Build Coastguard Worker with in_ifc as v: 63*387f9dfdSAndroid Build Coastguard Worker v.net_ns_fd = ns_ipdb.nl.netns 64*387f9dfdSAndroid Build Coastguard Worker except KeyboardInterrupt: 65*387f9dfdSAndroid Build Coastguard Worker # explicitly remove the interface 66*387f9dfdSAndroid Build Coastguard Worker out_ifname = "%sa" % ifc_base_name 67*387f9dfdSAndroid Build Coastguard Worker if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() 68*387f9dfdSAndroid Build Coastguard Worker raise 69*387f9dfdSAndroid Build Coastguard Worker 70*387f9dfdSAndroid Build Coastguard Worker if out_ifc: out_ifc.up().commit() 71*387f9dfdSAndroid Build Coastguard Worker try: 72*387f9dfdSAndroid Build Coastguard Worker # this is a workaround for fc31 and possible other disto's. 73*387f9dfdSAndroid Build Coastguard Worker # when interface 'lo' is already up, do another 'up().commit()' 74*387f9dfdSAndroid Build Coastguard Worker # has issues in fc31. 75*387f9dfdSAndroid Build Coastguard Worker # the workaround may become permanent if we upgrade pyroute2 76*387f9dfdSAndroid Build Coastguard Worker # in all machines. 77*387f9dfdSAndroid Build Coastguard Worker if 'state' in ns_ipdb.interfaces.lo.keys(): 78*387f9dfdSAndroid Build Coastguard Worker if ns_ipdb.interfaces.lo['state'] != 'up': 79*387f9dfdSAndroid Build Coastguard Worker ns_ipdb.interfaces.lo.up().commit() 80*387f9dfdSAndroid Build Coastguard Worker else: 81*387f9dfdSAndroid Build Coastguard Worker ns_ipdb.interfaces.lo.up().commit() 82*387f9dfdSAndroid Build Coastguard Worker except pyroute2.ipdb.exceptions.CommitException: 83*387f9dfdSAndroid Build Coastguard Worker print("Warning, commit for lo failed, operstate may be unknown") 84*387f9dfdSAndroid Build Coastguard Worker ns_ipdb.initdb() 85*387f9dfdSAndroid Build Coastguard Worker in_ifc = ns_ipdb.interfaces[in_ifname] 86*387f9dfdSAndroid Build Coastguard Worker with in_ifc as v: 87*387f9dfdSAndroid Build Coastguard Worker v.ifname = ns_ifc 88*387f9dfdSAndroid Build Coastguard Worker if ipaddr: v.add_ip("%s" % ipaddr) 89*387f9dfdSAndroid Build Coastguard Worker if macaddr: v.address = macaddr 90*387f9dfdSAndroid Build Coastguard Worker v.up() 91*387f9dfdSAndroid Build Coastguard Worker if disable_ipv6: 92*387f9dfdSAndroid Build Coastguard Worker cmd1 = ["sysctl", "-q", "-w", 93*387f9dfdSAndroid Build Coastguard Worker "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname] 94*387f9dfdSAndroid Build Coastguard Worker subprocess.call(cmd1) 95*387f9dfdSAndroid Build Coastguard Worker if fn and out_ifc: 96*387f9dfdSAndroid Build Coastguard Worker self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") 97*387f9dfdSAndroid Build Coastguard Worker self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", 98*387f9dfdSAndroid Build Coastguard Worker fd=fn.fd, name=fn.name, parent="ffff:", 99*387f9dfdSAndroid Build Coastguard Worker action=action, classid=1) 100*387f9dfdSAndroid Build Coastguard Worker if cmd: 101*387f9dfdSAndroid Build Coastguard Worker self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) 102*387f9dfdSAndroid Build Coastguard Worker return (ns_ipdb, out_ifc, in_ifc) 103*387f9dfdSAndroid Build Coastguard Worker 104*387f9dfdSAndroid Build Coastguard Worker # helper function to create a namespace and a veth connecting it 105*387f9dfdSAndroid Build Coastguard Worker def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None, 106*387f9dfdSAndroid Build Coastguard Worker macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): 107*387f9dfdSAndroid Build Coastguard Worker (ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc, 108*387f9dfdSAndroid Build Coastguard Worker ipaddr, macaddr, fn, cmd, action, 109*387f9dfdSAndroid Build Coastguard Worker disable_ipv6) 110*387f9dfdSAndroid Build Coastguard Worker return (ns_ipdb, out_ifc, in_ifc) 111*387f9dfdSAndroid Build Coastguard Worker 112*387f9dfdSAndroid Build Coastguard Worker def release(self): 113*387f9dfdSAndroid Build Coastguard Worker if self.released: return 114*387f9dfdSAndroid Build Coastguard Worker self.released = True 115*387f9dfdSAndroid Build Coastguard Worker for p in self.processes: 116*387f9dfdSAndroid Build Coastguard Worker if p.released: continue 117*387f9dfdSAndroid Build Coastguard Worker try: 118*387f9dfdSAndroid Build Coastguard Worker p.kill() 119*387f9dfdSAndroid Build Coastguard Worker p.wait() 120*387f9dfdSAndroid Build Coastguard Worker except: 121*387f9dfdSAndroid Build Coastguard Worker pass 122*387f9dfdSAndroid Build Coastguard Worker finally: 123*387f9dfdSAndroid Build Coastguard Worker p.release() 124*387f9dfdSAndroid Build Coastguard Worker for name, db in self.ipdbs.items(): db.release() 125*387f9dfdSAndroid Build Coastguard Worker for ns in self.namespaces: ns.remove() 126*387f9dfdSAndroid Build Coastguard Worker 127