• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import os
2import subprocess
3import pyroute2
4from pyroute2 import IPRoute, NetNS, IPDB, NSPopen
5
6class 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
19    # helper function to add additional ifc to namespace
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
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):
26        if name in self.ipdbs:
27            ns_ipdb = self.ipdbs[name]
28        else:
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
38            if disable_ipv6:
39                cmd1 = ["sysctl", "-q", "-w",
40                       "net.ipv6.conf.default.disable_ipv6=1"]
41                nsp = NSPopen(ns_ipdb.nl.netns, cmd1)
42                nsp.wait(); nsp.release()
43            ns_ipdb.interfaces.lo.up().commit()
44        if in_ifc:
45            in_ifname = in_ifc.ifname
46            with in_ifc as v:
47                # move half of veth into namespace
48                v.net_ns_fd = ns_ipdb.nl.netns
49        else:
50            # delete the potentially leaf-over veth interfaces
51            ipr = IPRoute()
52            for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link_remove(i)
53            ipr.close()
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
67        if out_ifc: out_ifc.up().commit()
68        ns_ipdb.interfaces.lo.up().commit()
69        ns_ipdb.initdb()
70        in_ifc = ns_ipdb.interfaces[in_ifname]
71        with in_ifc as v:
72            v.ifname = ns_ifc
73            if ipaddr: v.add_ip("%s" % ipaddr)
74            if macaddr: v.address = macaddr
75            v.up()
76        if disable_ipv6:
77            cmd1 = ["sysctl", "-q", "-w",
78                   "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname]
79            subprocess.call(cmd1)
80        if fn and out_ifc:
81            self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:")
82            self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1",
83                            fd=fn.fd, name=fn.name, parent="ffff:",
84                            action=action, classid=1)
85        if cmd:
86            self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd))
87        return (ns_ipdb, out_ifc, in_ifc)
88
89    # helper function to create a namespace and a veth connecting it
90    def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None,
91                   macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False):
92        (ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc,
93                                                      ipaddr, macaddr, fn, cmd, action,
94                                                      disable_ipv6)
95        return (ns_ipdb, out_ifc, in_ifc)
96
97    def release(self):
98        if self.released: return
99        self.released = True
100        for p in self.processes:
101            if p.released: continue
102            try:
103                p.kill()
104                p.wait()
105            except:
106                pass
107            finally:
108                p.release()
109        for name, db in self.ipdbs.items(): db.release()
110        for ns in self.namespaces: ns.remove()
111
112