xref: /aosp_15_r20/external/bcc/examples/networking/simulation.py (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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