Yonghong Song | 1bd744d | 2015-08-10 17:33:24 -0700 | [diff] [blame] | 1 | import os |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 2 | import subprocess |
Yonghong Song | 8cb8e96 | 2015-08-10 11:35:28 -0700 | [diff] [blame] | 3 | import pyroute2 |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 4 | from pyroute2 import IPRoute, NetNS, IPDB, NSPopen |
| 5 | |
| 6 | class Simulation(object): |
| 7 | """ |
| 8 | Helper class for controlling multiple namespaces. Inherit from |
| 9 | this class and setup your namespaces. |
| 10 | """ |
| 11 | |
| 12 | def __init__(self, ipdb): |
| 13 | self.ipdb = ipdb |
| 14 | self.ipdbs = {} |
| 15 | self.namespaces = [] |
| 16 | self.processes = [] |
| 17 | self.released = False |
| 18 | |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 19 | # helper function to add additional ifc to namespace |
Yonghong Song | a51ec2f | 2015-07-10 22:58:59 -0700 | [diff] [blame] | 20 | # if called directly outside Simulation class, "ifc_base_name" should be |
| 21 | # different from "name", the "ifc_base_name" and "name" are the same for |
| 22 | # the first ifc created by namespace |
Brenden Blanco | a3ef55b | 2015-07-13 21:10:39 -0700 | [diff] [blame] | 23 | def _ns_add_ifc(self, name, ns_ifc, ifc_base_name=None, in_ifc=None, |
| 24 | out_ifc=None, ipaddr=None, macaddr=None, fn=None, cmd=None, |
| 25 | action="ok", disable_ipv6=False): |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 26 | if name in self.ipdbs: |
| 27 | ns_ipdb = self.ipdbs[name] |
| 28 | else: |
Yonghong Song | c90bece | 2015-08-04 11:10:31 -0700 | [diff] [blame] | 29 | try: |
| 30 | nl=NetNS(name) |
| 31 | self.namespaces.append(nl) |
| 32 | except KeyboardInterrupt: |
| 33 | # remove the namespace if it has been created |
| 34 | pyroute2.netns.remove(name) |
| 35 | raise |
| 36 | ns_ipdb = IPDB(nl) |
| 37 | self.ipdbs[nl.netns] = ns_ipdb |
Brenden Blanco | a3ef55b | 2015-07-13 21:10:39 -0700 | [diff] [blame] | 38 | if disable_ipv6: |
Yonghong Song | 8cb8e96 | 2015-08-10 11:35:28 -0700 | [diff] [blame] | 39 | cmd1 = ["sysctl", "-q", "-w", |
Brenden Blanco | 9d2f33a | 2015-07-13 22:44:16 -0700 | [diff] [blame] | 40 | "net.ipv6.conf.default.disable_ipv6=1"] |
Yonghong Song | 8cb8e96 | 2015-08-10 11:35:28 -0700 | [diff] [blame] | 41 | nsp = NSPopen(ns_ipdb.nl.netns, cmd1) |
Brenden Blanco | a3ef55b | 2015-07-13 21:10:39 -0700 | [diff] [blame] | 42 | nsp.wait(); nsp.release() |
| 43 | ns_ipdb.interfaces.lo.up().commit() |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 44 | if in_ifc: |
| 45 | in_ifname = in_ifc.ifname |
Yonghong Song | c90bece | 2015-08-04 11:10:31 -0700 | [diff] [blame] | 46 | with in_ifc as v: |
| 47 | # move half of veth into namespace |
| 48 | v.net_ns_fd = ns_ipdb.nl.netns |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 49 | else: |
Yonghong Song | 1bd744d | 2015-08-10 17:33:24 -0700 | [diff] [blame] | 50 | # delete the potentially leaf-over veth interfaces |
Yonghong Song | 83f800f | 2015-08-10 19:04:58 -0700 | [diff] [blame] | 51 | ipr = IPRoute() |
| 52 | for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link_remove(i) |
Yonghong Song | df73a82 | 2015-08-12 08:21:57 -0700 | [diff] [blame] | 53 | ipr.close() |
Yonghong Song | c90bece | 2015-08-04 11:10:31 -0700 | [diff] [blame] | 54 | try: |
| 55 | out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth", |
| 56 | peer="%sb" % ifc_base_name).commit() |
| 57 | in_ifc = self.ipdb.interfaces[out_ifc.peer] |
| 58 | in_ifname = in_ifc.ifname |
| 59 | with in_ifc as v: |
| 60 | v.net_ns_fd = ns_ipdb.nl.netns |
| 61 | except KeyboardInterrupt: |
| 62 | # explicitly remove the interface |
| 63 | out_ifname = "%sa" % ifc_base_name |
| 64 | if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit() |
| 65 | raise |
| 66 | |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 67 | if out_ifc: out_ifc.up().commit() |
Brenden Blanco | fd4e4a5 | 2015-07-13 20:57:34 -0700 | [diff] [blame] | 68 | ns_ipdb.interfaces.lo.up().commit() |
Yonghong Song | c90bece | 2015-08-04 11:10:31 -0700 | [diff] [blame] | 69 | in_ifc = ns_ipdb.interfaces[in_ifname] |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 70 | with in_ifc as v: |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 71 | v.ifname = ns_ifc |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 72 | if ipaddr: v.add_ip("%s" % ipaddr) |
| 73 | if macaddr: v.address = macaddr |
| 74 | v.up() |
Brenden Blanco | 9d2f33a | 2015-07-13 22:44:16 -0700 | [diff] [blame] | 75 | if disable_ipv6: |
Yonghong Song | 8cb8e96 | 2015-08-10 11:35:28 -0700 | [diff] [blame] | 76 | cmd1 = ["sysctl", "-q", "-w", |
Brenden Blanco | 9d2f33a | 2015-07-13 22:44:16 -0700 | [diff] [blame] | 77 | "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname] |
Yonghong Song | 8cb8e96 | 2015-08-10 11:35:28 -0700 | [diff] [blame] | 78 | subprocess.call(cmd1) |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 79 | if fn and out_ifc: |
| 80 | self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:") |
| 81 | self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1", |
| 82 | fd=fn.fd, name=fn.name, parent="ffff:", |
| 83 | action=action, classid=1) |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 84 | if cmd: |
| 85 | self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd)) |
| 86 | return (ns_ipdb, out_ifc, in_ifc) |
| 87 | |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 88 | # helper function to create a namespace and a veth connecting it |
| 89 | def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None, |
| 90 | macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False): |
Yonghong Song | a51ec2f | 2015-07-10 22:58:59 -0700 | [diff] [blame] | 91 | (ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc, |
| 92 | ipaddr, macaddr, fn, cmd, action, |
| 93 | disable_ipv6) |
Yonghong Song | 485af95 | 2015-07-10 10:12:47 -0700 | [diff] [blame] | 94 | return (ns_ipdb, out_ifc, in_ifc) |
| 95 | |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 96 | def release(self): |
| 97 | if self.released: return |
| 98 | self.released = True |
| 99 | for p in self.processes: |
| 100 | if p.released: continue |
Brenden Blanco | fd4e4a5 | 2015-07-13 20:57:34 -0700 | [diff] [blame] | 101 | try: |
| 102 | p.kill() |
| 103 | p.wait() |
| 104 | except: |
| 105 | pass |
| 106 | finally: |
| 107 | p.release() |
Brenden Blanco | 085379b | 2015-06-18 00:28:47 -0700 | [diff] [blame] | 108 | for name, db in self.ipdbs.items(): db.release() |
| 109 | for ns in self.namespaces: ns.remove() |
| 110 | |