blob: 794a4f88b3d2030dc741eee4fb322599a6148f76 [file] [log] [blame]
Yonghong Song1bd744d2015-08-10 17:33:24 -07001import os
Yonghong Song485af952015-07-10 10:12:47 -07002import subprocess
Yonghong Song8cb8e962015-08-10 11:35:28 -07003import pyroute2
Brenden Blanco085379b2015-06-18 00:28:47 -07004from 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
Yonghong Song485af952015-07-10 10:12:47 -070019 # helper function to add additional ifc to namespace
Yonghong Songa51ec2f2015-07-10 22:58:59 -070020 # 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 Blancoa3ef55b2015-07-13 21:10:39 -070023 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 Song485af952015-07-10 10:12:47 -070026 if name in self.ipdbs:
27 ns_ipdb = self.ipdbs[name]
28 else:
Yonghong Songc90bece2015-08-04 11:10:31 -070029 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 Blancoa3ef55b2015-07-13 21:10:39 -070038 if disable_ipv6:
Yonghong Song8cb8e962015-08-10 11:35:28 -070039 cmd1 = ["sysctl", "-q", "-w",
Brenden Blanco9d2f33a2015-07-13 22:44:16 -070040 "net.ipv6.conf.default.disable_ipv6=1"]
Yonghong Song8cb8e962015-08-10 11:35:28 -070041 nsp = NSPopen(ns_ipdb.nl.netns, cmd1)
Brenden Blancoa3ef55b2015-07-13 21:10:39 -070042 nsp.wait(); nsp.release()
Dale Hamela47c44f2020-01-20 13:22:55 -050043 try:
44 ns_ipdb.interfaces.lo.up().commit()
45 except pyroute2.ipdb.exceptions.CommitException:
46 print("Warning, commit for lo failed, operstate may be unknown")
Brenden Blanco085379b2015-06-18 00:28:47 -070047 if in_ifc:
48 in_ifname = in_ifc.ifname
Yonghong Songc90bece2015-08-04 11:10:31 -070049 with in_ifc as v:
50 # move half of veth into namespace
51 v.net_ns_fd = ns_ipdb.nl.netns
Brenden Blanco085379b2015-06-18 00:28:47 -070052 else:
Yonghong Song1bd744d2015-08-10 17:33:24 -070053 # delete the potentially leaf-over veth interfaces
Yonghong Song83f800f2015-08-10 19:04:58 -070054 ipr = IPRoute()
Gary Ching-Pang Lin7fdaa9c2019-03-08 01:52:06 +080055 for i in ipr.link_lookup(ifname='%sa' % ifc_base_name): ipr.link("del", index=i)
Yonghong Songdf73a822015-08-12 08:21:57 -070056 ipr.close()
Yonghong Songc90bece2015-08-04 11:10:31 -070057 try:
58 out_ifc = self.ipdb.create(ifname="%sa" % ifc_base_name, kind="veth",
59 peer="%sb" % ifc_base_name).commit()
60 in_ifc = self.ipdb.interfaces[out_ifc.peer]
61 in_ifname = in_ifc.ifname
62 with in_ifc as v:
63 v.net_ns_fd = ns_ipdb.nl.netns
64 except KeyboardInterrupt:
65 # explicitly remove the interface
66 out_ifname = "%sa" % ifc_base_name
67 if out_ifname in self.ipdb.interfaces: self.ipdb.interfaces[out_ifname].remove().commit()
68 raise
69
Brenden Blanco085379b2015-06-18 00:28:47 -070070 if out_ifc: out_ifc.up().commit()
Dale Hamela47c44f2020-01-20 13:22:55 -050071 try:
72 # this is a workaround for fc31 and possible other disto's.
73 # when interface 'lo' is already up, do another 'up().commit()'
74 # has issues in fc31.
75 # the workaround may become permanent if we upgrade pyroute2
76 # in all machines.
77 if 'state' in ns_ipdb.interfaces.lo.keys():
78 if ns_ipdb.interfaces.lo['state'] != 'up':
79 ns_ipdb.interfaces.lo.up().commit()
80 else:
Yonghong Songdbfb1882020-01-21 23:49:08 -080081 ns_ipdb.interfaces.lo.up().commit()
Dale Hamela47c44f2020-01-20 13:22:55 -050082 except pyroute2.ipdb.exceptions.CommitException:
83 print("Warning, commit for lo failed, operstate may be unknown")
Brenden Blanco97a0cac2017-05-18 09:57:42 -070084 ns_ipdb.initdb()
Yonghong Songc90bece2015-08-04 11:10:31 -070085 in_ifc = ns_ipdb.interfaces[in_ifname]
Brenden Blanco085379b2015-06-18 00:28:47 -070086 with in_ifc as v:
Yonghong Song485af952015-07-10 10:12:47 -070087 v.ifname = ns_ifc
Brenden Blanco085379b2015-06-18 00:28:47 -070088 if ipaddr: v.add_ip("%s" % ipaddr)
89 if macaddr: v.address = macaddr
90 v.up()
Brenden Blanco9d2f33a2015-07-13 22:44:16 -070091 if disable_ipv6:
Yonghong Song8cb8e962015-08-10 11:35:28 -070092 cmd1 = ["sysctl", "-q", "-w",
Brenden Blanco9d2f33a2015-07-13 22:44:16 -070093 "net.ipv6.conf.%s.disable_ipv6=1" % out_ifc.ifname]
Yonghong Song8cb8e962015-08-10 11:35:28 -070094 subprocess.call(cmd1)
Brenden Blanco085379b2015-06-18 00:28:47 -070095 if fn and out_ifc:
96 self.ipdb.nl.tc("add", "ingress", out_ifc["index"], "ffff:")
97 self.ipdb.nl.tc("add-filter", "bpf", out_ifc["index"], ":1",
98 fd=fn.fd, name=fn.name, parent="ffff:",
99 action=action, classid=1)
Brenden Blanco085379b2015-06-18 00:28:47 -0700100 if cmd:
101 self.processes.append(NSPopen(ns_ipdb.nl.netns, cmd))
102 return (ns_ipdb, out_ifc, in_ifc)
103
Yonghong Song485af952015-07-10 10:12:47 -0700104 # helper function to create a namespace and a veth connecting it
105 def _create_ns(self, name, in_ifc=None, out_ifc=None, ipaddr=None,
106 macaddr=None, fn=None, cmd=None, action="ok", disable_ipv6=False):
Yonghong Songa51ec2f2015-07-10 22:58:59 -0700107 (ns_ipdb, out_ifc, in_ifc) = self._ns_add_ifc(name, "eth0", name, in_ifc, out_ifc,
108 ipaddr, macaddr, fn, cmd, action,
109 disable_ipv6)
Yonghong Song485af952015-07-10 10:12:47 -0700110 return (ns_ipdb, out_ifc, in_ifc)
111
Brenden Blanco085379b2015-06-18 00:28:47 -0700112 def release(self):
113 if self.released: return
114 self.released = True
115 for p in self.processes:
116 if p.released: continue
Brenden Blancofd4e4a52015-07-13 20:57:34 -0700117 try:
118 p.kill()
119 p.wait()
120 except:
121 pass
122 finally:
123 p.release()
Brenden Blanco085379b2015-06-18 00:28:47 -0700124 for name, db in self.ipdbs.items(): db.release()
125 for ns in self.namespaces: ns.remove()
126