18eb2bee6SAlexander V. Chernikov#!/usr/local/bin/python3 2cfc9cf9bSAlexander V. Chernikovimport copy 3cfc9cf9bSAlexander V. Chernikovimport ipaddress 48eb2bee6SAlexander V. Chernikovimport os 5584ad412SAlexander V. Chernikovimport re 68eb2bee6SAlexander V. Chernikovimport socket 7cfc9cf9bSAlexander V. Chernikovimport sys 88eb2bee6SAlexander V. Chernikovimport time 9584ad412SAlexander V. Chernikovfrom multiprocessing import connection 10cfc9cf9bSAlexander V. Chernikovfrom multiprocessing import Pipe 11cfc9cf9bSAlexander V. Chernikovfrom multiprocessing import Process 12cfc9cf9bSAlexander V. Chernikovfrom typing import Dict 138eb2bee6SAlexander V. Chernikovfrom typing import List 14cfc9cf9bSAlexander V. Chernikovfrom typing import NamedTuple 158eb2bee6SAlexander V. Chernikov 16cfc9cf9bSAlexander V. Chernikovfrom atf_python.sys.net.tools import ToolsHelper 17f63825ffSAlexander V. Chernikovfrom atf_python.utils import BaseTest 18f63825ffSAlexander V. Chernikovfrom atf_python.utils import libc 198eb2bee6SAlexander V. Chernikov 20cfc9cf9bSAlexander V. Chernikov 21cfc9cf9bSAlexander V. Chernikovdef run_cmd(cmd: str, verbose=True) -> str: 227964a28cSJose Luis Duran if verbose: 238eb2bee6SAlexander V. Chernikov print("run: '{}'".format(cmd)) 248eb2bee6SAlexander V. Chernikov return os.popen(cmd).read() 258eb2bee6SAlexander V. Chernikov 268eb2bee6SAlexander V. Chernikov 27f63825ffSAlexander V. Chernikovdef get_topology_id(test_id: str) -> str: 28f63825ffSAlexander V. Chernikov """ 29f63825ffSAlexander V. Chernikov Gets a unique topology id based on the pytest test_id. 30f63825ffSAlexander V. Chernikov "test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif]" -> 31f63825ffSAlexander V. Chernikov "TestIP6Output:test_output6_pktinfo[ipandif]" 32f63825ffSAlexander V. Chernikov """ 33f63825ffSAlexander V. Chernikov return ":".join(test_id.split("::")[-2:]) 34f63825ffSAlexander V. Chernikov 35f63825ffSAlexander V. Chernikov 36cfc9cf9bSAlexander V. Chernikovdef convert_test_name(test_name: str) -> str: 37cfc9cf9bSAlexander V. Chernikov """Convert test name to a string that can be used in the file/jail names""" 38cfc9cf9bSAlexander V. Chernikov ret = "" 39cfc9cf9bSAlexander V. Chernikov for char in test_name: 40f63825ffSAlexander V. Chernikov if char.isalnum() or char in ("_", "-", ":"): 41cfc9cf9bSAlexander V. Chernikov ret += char 42cfc9cf9bSAlexander V. Chernikov elif char in ("["): 43cfc9cf9bSAlexander V. Chernikov ret += "_" 44cfc9cf9bSAlexander V. Chernikov return ret 458eb2bee6SAlexander V. Chernikov 46cfc9cf9bSAlexander V. Chernikov 47cfc9cf9bSAlexander V. Chernikovclass VnetInterface(object): 488eb2bee6SAlexander V. Chernikov # defines from net/if_types.h 498eb2bee6SAlexander V. Chernikov IFT_LOOP = 0x18 508eb2bee6SAlexander V. Chernikov IFT_ETHER = 0x06 518eb2bee6SAlexander V. Chernikov 52cfc9cf9bSAlexander V. Chernikov def __init__(self, iface_alias: str, iface_name: str): 538eb2bee6SAlexander V. Chernikov self.name = iface_name 54cfc9cf9bSAlexander V. Chernikov self.alias = iface_alias 558eb2bee6SAlexander V. Chernikov self.vnet_name = "" 568eb2bee6SAlexander V. Chernikov self.jailed = False 57cfc9cf9bSAlexander V. Chernikov self.addr_map: Dict[str, Dict] = {"inet6": {}, "inet": {}} 58cfc9cf9bSAlexander V. Chernikov self.prefixes4: List[List[str]] = [] 59cfc9cf9bSAlexander V. Chernikov self.prefixes6: List[List[str]] = [] 60*a8b8feceSMark Johnston self.fib: int 618eb2bee6SAlexander V. Chernikov if iface_name.startswith("lo"): 628eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_LOOP 638eb2bee6SAlexander V. Chernikov else: 648eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_ETHER 659c95fcb7SRonald Klop self.ether = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % iface_name).rstrip() 668eb2bee6SAlexander V. Chernikov 678eb2bee6SAlexander V. Chernikov @property 688eb2bee6SAlexander V. Chernikov def ifindex(self): 698eb2bee6SAlexander V. Chernikov return socket.if_nametoindex(self.name) 708eb2bee6SAlexander V. Chernikov 71cfc9cf9bSAlexander V. Chernikov @property 72cfc9cf9bSAlexander V. Chernikov def first_ipv6(self): 73cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet6"] 74cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 75cfc9cf9bSAlexander V. Chernikov 76cfc9cf9bSAlexander V. Chernikov @property 77cfc9cf9bSAlexander V. Chernikov def first_ipv4(self): 78cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet"] 79cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 80cfc9cf9bSAlexander V. Chernikov 818eb2bee6SAlexander V. Chernikov def set_vnet(self, vnet_name: str): 828eb2bee6SAlexander V. Chernikov self.vnet_name = vnet_name 838eb2bee6SAlexander V. Chernikov 848eb2bee6SAlexander V. Chernikov def set_jailed(self, jailed: bool): 858eb2bee6SAlexander V. Chernikov self.jailed = jailed 868eb2bee6SAlexander V. Chernikov 87a1eb150cSJose Luis Duran def run_cmd(self, cmd, verbose=False): 888eb2bee6SAlexander V. Chernikov if self.vnet_name and not self.jailed: 89a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.vnet_name, cmd) 90cfc9cf9bSAlexander V. Chernikov return run_cmd(cmd, verbose) 918eb2bee6SAlexander V. Chernikov 928eb2bee6SAlexander V. Chernikov @classmethod 93cfc9cf9bSAlexander V. Chernikov def setup_loopback(cls, vnet_name: str): 94cfc9cf9bSAlexander V. Chernikov lo = VnetInterface("", "lo0") 95cfc9cf9bSAlexander V. Chernikov lo.set_vnet(vnet_name) 964856aeaaSJose Luis Duran lo.setup_addr("127.0.0.1/8") 97cfc9cf9bSAlexander V. Chernikov lo.turn_up() 98cfc9cf9bSAlexander V. Chernikov 99cfc9cf9bSAlexander V. Chernikov @classmethod 100cfc9cf9bSAlexander V. Chernikov def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: 1018eb2bee6SAlexander V. Chernikov name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() 1028eb2bee6SAlexander V. Chernikov if not name: 1038eb2bee6SAlexander V. Chernikov raise Exception("Unable to create iface {}".format(iface_name)) 1049c95fcb7SRonald Klop if1 = cls(alias_name, name) 1059c95fcb7SRonald Klop ret = [if1] 1068eb2bee6SAlexander V. Chernikov if name.startswith("epair"): 107bd8296e7SMichael Tuexen run_cmd("/sbin/ifconfig {} -txcsum -txcsum6".format(name)) 1089c95fcb7SRonald Klop if2 = cls(alias_name, name[:-1] + "b") 1099c95fcb7SRonald Klop if1.epairb = if2 110*a8b8feceSMark Johnston ret.append(if2) 111cfc9cf9bSAlexander V. Chernikov return ret 1128eb2bee6SAlexander V. Chernikov 1134f35a84bSKristof Provost def set_mtu(self, mtu): 1144f35a84bSKristof Provost run_cmd("/sbin/ifconfig {} mtu {}".format(self.name, mtu)) 1154f35a84bSKristof Provost 116cfc9cf9bSAlexander V. Chernikov def setup_addr(self, _addr: str): 117cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_interface(_addr) 118cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 1198eb2bee6SAlexander V. Chernikov family = "inet6" 1207064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1218eb2bee6SAlexander V. Chernikov else: 1228eb2bee6SAlexander V. Chernikov family = "inet" 1237064c94aSAlexander V. Chernikov if self.addr_map[family]: 1247064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} alias {}".format(self.name, addr) 1257064c94aSAlexander V. Chernikov else: 1268eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1278eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1287064c94aSAlexander V. Chernikov self.addr_map[family][str(addr.ip)] = addr 1298eb2bee6SAlexander V. Chernikov 130cfc9cf9bSAlexander V. Chernikov def delete_addr(self, _addr: str): 131cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_address(_addr) 132cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 133cfc9cf9bSAlexander V. Chernikov family = "inet6" 1348eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6 {} delete".format(self.name, addr) 1358eb2bee6SAlexander V. Chernikov else: 136cfc9cf9bSAlexander V. Chernikov family = "inet" 1378eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} -alias {}".format(self.name, addr) 1388eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 139cfc9cf9bSAlexander V. Chernikov del self.addr_map[family][str(addr)] 1408eb2bee6SAlexander V. Chernikov 1418eb2bee6SAlexander V. Chernikov def turn_up(self): 1428eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} up".format(self.name) 1438eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1448eb2bee6SAlexander V. Chernikov 145*a8b8feceSMark Johnston def setfib(self, fib: int): 146*a8b8feceSMark Johnston cmd = "/sbin/ifconfig {} fib {}".format(self.name, fib) 147*a8b8feceSMark Johnston self.run_cmd(cmd) 148*a8b8feceSMark Johnston 1498eb2bee6SAlexander V. Chernikov def enable_ipv6(self): 1506ae89b2fSKristof Provost cmd = "/usr/sbin/ndp -i {} -- -disabled".format(self.name) 1518eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1528eb2bee6SAlexander V. Chernikov 153cfc9cf9bSAlexander V. Chernikov def has_tentative(self) -> bool: 154cfc9cf9bSAlexander V. Chernikov """True if an interface has some addresses in tenative state""" 155cfc9cf9bSAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6".format(self.name) 156cfc9cf9bSAlexander V. Chernikov out = self.run_cmd(cmd, verbose=False) 157cfc9cf9bSAlexander V. Chernikov for line in out.splitlines(): 158cfc9cf9bSAlexander V. Chernikov if "tentative" in line: 1598eb2bee6SAlexander V. Chernikov return True 1608eb2bee6SAlexander V. Chernikov return False 1618eb2bee6SAlexander V. Chernikov 1628eb2bee6SAlexander V. Chernikov 163cfc9cf9bSAlexander V. Chernikovclass IfaceFactory(object): 164cfc9cf9bSAlexander V. Chernikov INTERFACES_FNAME = "created_ifaces.lst" 165f3065e76SAlexander V. Chernikov AUTODELETE_TYPES = ("epair", "gif", "gre", "lo", "tap", "tun") 166cfc9cf9bSAlexander V. Chernikov 167f63825ffSAlexander V. Chernikov def __init__(self): 168cfc9cf9bSAlexander V. Chernikov self.file_name = self.INTERFACES_FNAME 169cfc9cf9bSAlexander V. Chernikov 170cfc9cf9bSAlexander V. Chernikov def _register_iface(self, iface_name: str): 171cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 172cfc9cf9bSAlexander V. Chernikov f.write(iface_name + "\n") 173cfc9cf9bSAlexander V. Chernikov 17420ea7f26SAlexander V. Chernikov def _list_ifaces(self) -> List[str]: 17520ea7f26SAlexander V. Chernikov ret: List[str] = [] 1768eb2bee6SAlexander V. Chernikov try: 177cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "r") as f: 1788eb2bee6SAlexander V. Chernikov for line in f: 17920ea7f26SAlexander V. Chernikov ret.append(line.strip()) 18020ea7f26SAlexander V. Chernikov except OSError: 18120ea7f26SAlexander V. Chernikov pass 18220ea7f26SAlexander V. Chernikov return ret 18320ea7f26SAlexander V. Chernikov 18420ea7f26SAlexander V. Chernikov def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: 18520ea7f26SAlexander V. Chernikov ifaces = VnetInterface.create_iface(alias_name, iface_name) 18620ea7f26SAlexander V. Chernikov for iface in ifaces: 18720ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface.name): 18820ea7f26SAlexander V. Chernikov self._register_iface(iface.name) 18920ea7f26SAlexander V. Chernikov return ifaces 19020ea7f26SAlexander V. Chernikov 19120ea7f26SAlexander V. Chernikov @staticmethod 19220ea7f26SAlexander V. Chernikov def is_autodeleted(iface_name: str) -> bool: 1932e620256SJose Luis Duran if iface_name == "lo0": 1942e620256SJose Luis Duran return False 19520ea7f26SAlexander V. Chernikov iface_type = re.split(r"\d+", iface_name)[0] 19620ea7f26SAlexander V. Chernikov return iface_type in IfaceFactory.AUTODELETE_TYPES 19720ea7f26SAlexander V. Chernikov 19820ea7f26SAlexander V. Chernikov def cleanup_vnet_interfaces(self, vnet_name: str) -> List[str]: 19920ea7f26SAlexander V. Chernikov """Destroys""" 20020ea7f26SAlexander V. Chernikov ifaces_lst = ToolsHelper.get_output( 201a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 20220ea7f26SAlexander V. Chernikov ) 20320ea7f26SAlexander V. Chernikov for iface_name in ifaces_lst.split(): 20420ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface_name): 20520ea7f26SAlexander V. Chernikov if iface_name not in self._list_ifaces(): 20620ea7f26SAlexander V. Chernikov print("Skipping interface {}:{}".format(vnet_name, iface_name)) 20720ea7f26SAlexander V. Chernikov continue 20820ea7f26SAlexander V. Chernikov run_cmd( 209a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig {} destroy".format(vnet_name, iface_name) 21020ea7f26SAlexander V. Chernikov ) 21120ea7f26SAlexander V. Chernikov 21220ea7f26SAlexander V. Chernikov def cleanup(self): 21320ea7f26SAlexander V. Chernikov try: 214cfc9cf9bSAlexander V. Chernikov os.unlink(self.INTERFACES_FNAME) 21520ea7f26SAlexander V. Chernikov except OSError: 2168eb2bee6SAlexander V. Chernikov pass 2178eb2bee6SAlexander V. Chernikov 2188eb2bee6SAlexander V. Chernikov 219cfc9cf9bSAlexander V. Chernikovclass VnetInstance(object): 220cfc9cf9bSAlexander V. Chernikov def __init__( 221cfc9cf9bSAlexander V. Chernikov self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] 222cfc9cf9bSAlexander V. Chernikov ): 223cfc9cf9bSAlexander V. Chernikov self.name = vnet_name 224cfc9cf9bSAlexander V. Chernikov self.alias = vnet_alias # reference in the test topology 225cfc9cf9bSAlexander V. Chernikov self.jid = jid 226cfc9cf9bSAlexander V. Chernikov self.ifaces = ifaces 227cfc9cf9bSAlexander V. Chernikov self.iface_alias_map = {} # iface.alias: iface 228cfc9cf9bSAlexander V. Chernikov self.iface_map = {} # iface.name: iface 2298eb2bee6SAlexander V. Chernikov for iface in ifaces: 230cfc9cf9bSAlexander V. Chernikov iface.set_vnet(vnet_name) 231cfc9cf9bSAlexander V. Chernikov iface.set_jailed(True) 232cfc9cf9bSAlexander V. Chernikov self.iface_alias_map[iface.alias] = iface 233cfc9cf9bSAlexander V. Chernikov self.iface_map[iface.name] = iface 234584ad412SAlexander V. Chernikov # Allow reference to interfce aliases as attributes 235584ad412SAlexander V. Chernikov setattr(self, iface.alias, iface) 236cfc9cf9bSAlexander V. Chernikov self.need_dad = False # Disable duplicate address detection by default 237cfc9cf9bSAlexander V. Chernikov self.attached = False 238cfc9cf9bSAlexander V. Chernikov self.pipe = None 239cfc9cf9bSAlexander V. Chernikov self.subprocess = None 240cfc9cf9bSAlexander V. Chernikov 2418a30ab53SJose Luis Duran def run_vnet_cmd(self, cmd, verbose=True): 242cfc9cf9bSAlexander V. Chernikov if not self.attached: 243a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.name, cmd) 2448a30ab53SJose Luis Duran return run_cmd(cmd, verbose) 245cfc9cf9bSAlexander V. Chernikov 246cfc9cf9bSAlexander V. Chernikov def disable_dad(self): 247cfc9cf9bSAlexander V. Chernikov self.run_vnet_cmd("/sbin/sysctl net.inet6.ip6.dad_count=0") 248cfc9cf9bSAlexander V. Chernikov 249cfc9cf9bSAlexander V. Chernikov def set_pipe(self, pipe): 250cfc9cf9bSAlexander V. Chernikov self.pipe = pipe 251cfc9cf9bSAlexander V. Chernikov 252cfc9cf9bSAlexander V. Chernikov def set_subprocess(self, p): 253cfc9cf9bSAlexander V. Chernikov self.subprocess = p 2548eb2bee6SAlexander V. Chernikov 2558eb2bee6SAlexander V. Chernikov @staticmethod 2568eb2bee6SAlexander V. Chernikov def attach_jid(jid: int): 2573873bdc2SAlexander V. Chernikov error_code = libc.jail_attach(jid) 2583873bdc2SAlexander V. Chernikov if error_code != 0: 2593873bdc2SAlexander V. Chernikov raise Exception("jail_attach() failed: errno {}".format(error_code)) 2608eb2bee6SAlexander V. Chernikov 2618eb2bee6SAlexander V. Chernikov def attach(self): 2628eb2bee6SAlexander V. Chernikov self.attach_jid(self.jid) 263cfc9cf9bSAlexander V. Chernikov self.attached = True 2648eb2bee6SAlexander V. Chernikov 2658eb2bee6SAlexander V. Chernikov 266cfc9cf9bSAlexander V. Chernikovclass VnetFactory(object): 267cfc9cf9bSAlexander V. Chernikov JAILS_FNAME = "created_jails.lst" 268cfc9cf9bSAlexander V. Chernikov 269f63825ffSAlexander V. Chernikov def __init__(self, topology_id: str): 270f63825ffSAlexander V. Chernikov self.topology_id = topology_id 271cfc9cf9bSAlexander V. Chernikov self.file_name = self.JAILS_FNAME 272cfc9cf9bSAlexander V. Chernikov self._vnets: List[str] = [] 273cfc9cf9bSAlexander V. Chernikov 274cfc9cf9bSAlexander V. Chernikov def _register_vnet(self, vnet_name: str): 275cfc9cf9bSAlexander V. Chernikov self._vnets.append(vnet_name) 276cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 277cfc9cf9bSAlexander V. Chernikov f.write(vnet_name + "\n") 278cfc9cf9bSAlexander V. Chernikov 279cfc9cf9bSAlexander V. Chernikov @staticmethod 280cfc9cf9bSAlexander V. Chernikov def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: 281a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 282cfc9cf9bSAlexander V. Chernikov not_matched: List[str] = [] 283cfc9cf9bSAlexander V. Chernikov for i in range(50): 284cfc9cf9bSAlexander V. Chernikov vnet_ifaces = run_cmd(cmd).strip().split(" ") 285cfc9cf9bSAlexander V. Chernikov not_matched = [] 286cfc9cf9bSAlexander V. Chernikov for iface_name in ifaces: 287cfc9cf9bSAlexander V. Chernikov if iface_name not in vnet_ifaces: 288cfc9cf9bSAlexander V. Chernikov not_matched.append(iface_name) 289cfc9cf9bSAlexander V. Chernikov if len(not_matched) == 0: 290cfc9cf9bSAlexander V. Chernikov return [] 291cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 292cfc9cf9bSAlexander V. Chernikov return not_matched 293cfc9cf9bSAlexander V. Chernikov 2942213e158SKristof Provost def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface], opts: List[str]): 295f63825ffSAlexander V. Chernikov vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) 296cfc9cf9bSAlexander V. Chernikov if self._vnets: 297cfc9cf9bSAlexander V. Chernikov # add number to distinguish jails 298cfc9cf9bSAlexander V. Chernikov vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) 299cfc9cf9bSAlexander V. Chernikov iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) 3002213e158SKristof Provost opt_cmds = " ".join(["{}".format(i) for i in opts]) 3012213e158SKristof Provost cmd = "/usr/sbin/jail -i -c name={} persist vnet {} {}".format( 3022213e158SKristof Provost vnet_name, iface_cmds, opt_cmds 303cfc9cf9bSAlexander V. Chernikov ) 304f63825ffSAlexander V. Chernikov jid = 0 305f63825ffSAlexander V. Chernikov try: 306cfc9cf9bSAlexander V. Chernikov jid_str = run_cmd(cmd) 307cfc9cf9bSAlexander V. Chernikov jid = int(jid_str) 30820ea7f26SAlexander V. Chernikov except ValueError: 309f63825ffSAlexander V. Chernikov print("Jail creation failed, output: {}".format(jid_str)) 310f63825ffSAlexander V. Chernikov raise 311cfc9cf9bSAlexander V. Chernikov self._register_vnet(vnet_name) 312cfc9cf9bSAlexander V. Chernikov 313cfc9cf9bSAlexander V. Chernikov # Run expedited version of routing 314cfc9cf9bSAlexander V. Chernikov VnetInterface.setup_loopback(vnet_name) 315cfc9cf9bSAlexander V. Chernikov 316cfc9cf9bSAlexander V. Chernikov not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) 317cfc9cf9bSAlexander V. Chernikov if not_found: 318cfc9cf9bSAlexander V. Chernikov raise Exception( 319cfc9cf9bSAlexander V. Chernikov "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) 320cfc9cf9bSAlexander V. Chernikov ) 321cfc9cf9bSAlexander V. Chernikov return VnetInstance(vnet_alias, vnet_name, jid, ifaces) 322cfc9cf9bSAlexander V. Chernikov 323cfc9cf9bSAlexander V. Chernikov def cleanup(self): 32420ea7f26SAlexander V. Chernikov iface_factory = IfaceFactory() 325cfc9cf9bSAlexander V. Chernikov try: 326cfc9cf9bSAlexander V. Chernikov with open(self.file_name) as f: 327cfc9cf9bSAlexander V. Chernikov for line in f: 328f63825ffSAlexander V. Chernikov vnet_name = line.strip() 32920ea7f26SAlexander V. Chernikov iface_factory.cleanup_vnet_interfaces(vnet_name) 330f63825ffSAlexander V. Chernikov run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) 331cfc9cf9bSAlexander V. Chernikov os.unlink(self.JAILS_FNAME) 332cfc9cf9bSAlexander V. Chernikov except OSError: 333cfc9cf9bSAlexander V. Chernikov pass 334cfc9cf9bSAlexander V. Chernikov 335cfc9cf9bSAlexander V. Chernikov 336cfc9cf9bSAlexander V. Chernikovclass SingleInterfaceMap(NamedTuple): 337cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] 338cfc9cf9bSAlexander V. Chernikov vnet_aliases: List[str] 339cfc9cf9bSAlexander V. Chernikov 340cfc9cf9bSAlexander V. Chernikov 341f63825ffSAlexander V. Chernikovclass ObjectsMap(NamedTuple): 342f63825ffSAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX 343f63825ffSAlexander V. Chernikov vnet_map: Dict[str, VnetInstance] # keyed by vnetX 344f63825ffSAlexander V. Chernikov topo_map: Dict # self.TOPOLOGY 345f63825ffSAlexander V. Chernikov 346f63825ffSAlexander V. Chernikov 3473873bdc2SAlexander V. Chernikovclass VnetTestTemplate(BaseTest): 3486332ef89SAlexander V. Chernikov NEED_ROOT: bool = True 349cfc9cf9bSAlexander V. Chernikov TOPOLOGY = {} 350cfc9cf9bSAlexander V. Chernikov 351ae8d5881SKristof Provost def _require_default_modules(self): 352ae8d5881SKristof Provost libc.kldload("if_epair.ko") 353ae8d5881SKristof Provost self.require_module("if_epair") 354ae8d5881SKristof Provost 355cfc9cf9bSAlexander V. Chernikov def _get_vnet_handler(self, vnet_alias: str): 356cfc9cf9bSAlexander V. Chernikov handler_name = "{}_handler".format(vnet_alias) 357cfc9cf9bSAlexander V. Chernikov return getattr(self, handler_name, None) 358cfc9cf9bSAlexander V. Chernikov 359cfc9cf9bSAlexander V. Chernikov def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): 360cfc9cf9bSAlexander V. Chernikov """Base Handler to setup given VNET. 361cfc9cf9bSAlexander V. Chernikov Can be run in a subprocess. If so, passes control to the special 362cfc9cf9bSAlexander V. Chernikov vnetX_handler() after setting up interface addresses 363cfc9cf9bSAlexander V. Chernikov """ 364cfc9cf9bSAlexander V. Chernikov vnet.attach() 36507940d1dSMark Johnston os.chdir(os.getenv("HOME")) 366cfc9cf9bSAlexander V. Chernikov print("# setup_vnet({})".format(vnet.name)) 367f63825ffSAlexander V. Chernikov if pipe is not None: 368f63825ffSAlexander V. Chernikov vnet.set_pipe(pipe) 369cfc9cf9bSAlexander V. Chernikov 370f63825ffSAlexander V. Chernikov topo = obj_map.topo_map 371cfc9cf9bSAlexander V. Chernikov ipv6_ifaces = [] 372cfc9cf9bSAlexander V. Chernikov # Disable DAD 373cfc9cf9bSAlexander V. Chernikov if not vnet.need_dad: 374cfc9cf9bSAlexander V. Chernikov vnet.disable_dad() 375cfc9cf9bSAlexander V. Chernikov for iface in vnet.ifaces: 376cfc9cf9bSAlexander V. Chernikov # check index of vnet within an interface 377cfc9cf9bSAlexander V. Chernikov # as we have prefixes for both ends of the interface 378f63825ffSAlexander V. Chernikov iface_map = obj_map.iface_map[iface.alias] 379cfc9cf9bSAlexander V. Chernikov idx = iface_map.vnet_aliases.index(vnet.alias) 380cfc9cf9bSAlexander V. Chernikov prefixes6 = topo[iface.alias].get("prefixes6", []) 381cfc9cf9bSAlexander V. Chernikov prefixes4 = topo[iface.alias].get("prefixes4", []) 3824f35a84bSKristof Provost mtu = topo[iface.alias].get("mtu", 0) 383*a8b8feceSMark Johnston if "fib" in topo[iface.alias]: 384*a8b8feceSMark Johnston fib = topo[iface.alias]["fib"] 385*a8b8feceSMark Johnston iface.setfib(fib[idx]) 386cfc9cf9bSAlexander V. Chernikov if prefixes6 or prefixes4: 387cfc9cf9bSAlexander V. Chernikov ipv6_ifaces.append(iface) 388cfc9cf9bSAlexander V. Chernikov iface.turn_up() 389cfc9cf9bSAlexander V. Chernikov if prefixes6: 390cfc9cf9bSAlexander V. Chernikov iface.enable_ipv6() 391cfc9cf9bSAlexander V. Chernikov for prefix in prefixes6 + prefixes4: 392584ad412SAlexander V. Chernikov if prefix[idx]: 393cfc9cf9bSAlexander V. Chernikov iface.setup_addr(prefix[idx]) 3944f35a84bSKristof Provost if mtu != 0: 3954f35a84bSKristof Provost iface.set_mtu(mtu) 396cfc9cf9bSAlexander V. Chernikov for iface in ipv6_ifaces: 397cfc9cf9bSAlexander V. Chernikov while iface.has_tentative(): 398cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 399cfc9cf9bSAlexander V. Chernikov 400cfc9cf9bSAlexander V. Chernikov # Run actual handler 401cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 402cfc9cf9bSAlexander V. Chernikov if handler: 403cfc9cf9bSAlexander V. Chernikov # Do unbuffered stdout for children 404cfc9cf9bSAlexander V. Chernikov # so the logs are present if the child hangs 405cfc9cf9bSAlexander V. Chernikov sys.stdout.reconfigure(line_buffering=True) 4066332ef89SAlexander V. Chernikov self.drop_privileges() 407f63825ffSAlexander V. Chernikov handler(vnet) 408cfc9cf9bSAlexander V. Chernikov 409584ad412SAlexander V. Chernikov def _get_topo_ifmap(self, topo: Dict): 410584ad412SAlexander V. Chernikov iface_factory = IfaceFactory() 411584ad412SAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] = {} 412584ad412SAlexander V. Chernikov iface_aliases = set() 413584ad412SAlexander V. Chernikov for obj_name, obj_data in topo.items(): 414584ad412SAlexander V. Chernikov if obj_name.startswith("vnet"): 415584ad412SAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 416584ad412SAlexander V. Chernikov iface_aliases.add(iface_alias) 417584ad412SAlexander V. Chernikov for iface_alias in iface_aliases: 418584ad412SAlexander V. Chernikov print("Creating {}".format(iface_alias)) 419584ad412SAlexander V. Chernikov iface_data = topo[iface_alias] 420584ad412SAlexander V. Chernikov iface_type = iface_data.get("type", "epair") 421584ad412SAlexander V. Chernikov ifaces = iface_factory.create_iface(iface_alias, iface_type) 422584ad412SAlexander V. Chernikov smap = SingleInterfaceMap(ifaces, []) 423584ad412SAlexander V. Chernikov iface_map[iface_alias] = smap 424584ad412SAlexander V. Chernikov return iface_map 425584ad412SAlexander V. Chernikov 426f63825ffSAlexander V. Chernikov def setup_topology(self, topo: Dict, topology_id: str): 427cfc9cf9bSAlexander V. Chernikov """Creates jails & interfaces for the provided topology""" 428cfc9cf9bSAlexander V. Chernikov vnet_map = {} 429f63825ffSAlexander V. Chernikov vnet_factory = VnetFactory(topology_id) 430584ad412SAlexander V. Chernikov iface_map = self._get_topo_ifmap(topo) 431cfc9cf9bSAlexander V. Chernikov for obj_name, obj_data in topo.items(): 432cfc9cf9bSAlexander V. Chernikov if obj_name.startswith("vnet"): 433cfc9cf9bSAlexander V. Chernikov vnet_ifaces = [] 434*a8b8feceSMark Johnston maxfib = 0 435cfc9cf9bSAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 436cfc9cf9bSAlexander V. Chernikov # epair creates 2 interfaces, grab first _available_ 437cfc9cf9bSAlexander V. Chernikov # and map it to the VNET being created 438cfc9cf9bSAlexander V. Chernikov idx = len(iface_map[iface_alias].vnet_aliases) 439cfc9cf9bSAlexander V. Chernikov iface_map[iface_alias].vnet_aliases.append(obj_name) 440cfc9cf9bSAlexander V. Chernikov vnet_ifaces.append(iface_map[iface_alias].ifaces[idx]) 441*a8b8feceSMark Johnston fib = topo[iface_alias].get("fib", (0, 0)) 442*a8b8feceSMark Johnston maxfib = max(maxfib, fib[idx]) 4432213e158SKristof Provost opts = [] 4442213e158SKristof Provost if "opts" in obj_data: 4452213e158SKristof Provost opts = obj_data["opts"] 4462213e158SKristof Provost vnet = vnet_factory.create_vnet(obj_name, vnet_ifaces, opts) 447*a8b8feceSMark Johnston if maxfib != 0: 448*a8b8feceSMark Johnston # Make sure the VNET has enough FIBs. 449*a8b8feceSMark Johnston vnet.run_vnet_cmd("/sbin/sysctl net.fibs={}".format(maxfib + 1)) 450cfc9cf9bSAlexander V. Chernikov vnet_map[obj_name] = vnet 451584ad412SAlexander V. Chernikov # Allow reference to VNETs as attributes 452584ad412SAlexander V. Chernikov setattr(self, obj_name, vnet) 453cfc9cf9bSAlexander V. Chernikov # Debug output 454cfc9cf9bSAlexander V. Chernikov print("============= TEST TOPOLOGY =============") 455cfc9cf9bSAlexander V. Chernikov for vnet_alias, vnet in vnet_map.items(): 456cfc9cf9bSAlexander V. Chernikov print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") 457cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 458cfc9cf9bSAlexander V. Chernikov if handler: 459cfc9cf9bSAlexander V. Chernikov print(" handler: {}".format(handler.__name__), end="") 460cfc9cf9bSAlexander V. Chernikov print() 461cfc9cf9bSAlexander V. Chernikov for iface_alias, iface_data in iface_map.items(): 462cfc9cf9bSAlexander V. Chernikov vnets = iface_data.vnet_aliases 463cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] = iface_data.ifaces 464cfc9cf9bSAlexander V. Chernikov if len(vnets) == 1 and len(ifaces) == 2: 465cfc9cf9bSAlexander V. Chernikov print( 466cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> main::{}".format( 467cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, ifaces[1].name 468cfc9cf9bSAlexander V. Chernikov ) 469cfc9cf9bSAlexander V. Chernikov ) 470cfc9cf9bSAlexander V. Chernikov elif len(vnets) == 2 and len(ifaces) == 2: 471cfc9cf9bSAlexander V. Chernikov print( 472cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> {}::{}".format( 473cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name 474cfc9cf9bSAlexander V. Chernikov ) 475cfc9cf9bSAlexander V. Chernikov ) 476cfc9cf9bSAlexander V. Chernikov else: 477cfc9cf9bSAlexander V. Chernikov print( 478cfc9cf9bSAlexander V. Chernikov "# iface {}: ifaces: {} vnets: {}".format( 479cfc9cf9bSAlexander V. Chernikov iface_alias, vnets, [i.name for i in ifaces] 480cfc9cf9bSAlexander V. Chernikov ) 481cfc9cf9bSAlexander V. Chernikov ) 482cfc9cf9bSAlexander V. Chernikov print() 483f63825ffSAlexander V. Chernikov return ObjectsMap(iface_map, vnet_map, topo) 484cfc9cf9bSAlexander V. Chernikov 485f63825ffSAlexander V. Chernikov def setup_method(self, _method): 486cfc9cf9bSAlexander V. Chernikov """Sets up all the required topology and handlers for the given test""" 487f63825ffSAlexander V. Chernikov super().setup_method(_method) 488ae8d5881SKristof Provost self._require_default_modules() 489ae8d5881SKristof Provost 490f63825ffSAlexander V. Chernikov # TestIP6Output.test_output6_pktinfo[ipandif] 491f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 492cfc9cf9bSAlexander V. Chernikov topology = self.TOPOLOGY 493cfc9cf9bSAlexander V. Chernikov # First, setup kernel objects - interfaces & vnets 494f63825ffSAlexander V. Chernikov obj_map = self.setup_topology(topology, topology_id) 495cfc9cf9bSAlexander V. Chernikov main_vnet = None # one without subprocess handler 496f63825ffSAlexander V. Chernikov for vnet_alias, vnet in obj_map.vnet_map.items(): 497cfc9cf9bSAlexander V. Chernikov if self._get_vnet_handler(vnet_alias): 498cfc9cf9bSAlexander V. Chernikov # Need subprocess to run 499cfc9cf9bSAlexander V. Chernikov parent_pipe, child_pipe = Pipe() 500cfc9cf9bSAlexander V. Chernikov p = Process( 501cfc9cf9bSAlexander V. Chernikov target=self._setup_vnet, 502cfc9cf9bSAlexander V. Chernikov args=( 503cfc9cf9bSAlexander V. Chernikov vnet, 504cfc9cf9bSAlexander V. Chernikov obj_map, 505cfc9cf9bSAlexander V. Chernikov child_pipe, 506cfc9cf9bSAlexander V. Chernikov ), 507cfc9cf9bSAlexander V. Chernikov ) 508cfc9cf9bSAlexander V. Chernikov vnet.set_pipe(parent_pipe) 509cfc9cf9bSAlexander V. Chernikov vnet.set_subprocess(p) 510cfc9cf9bSAlexander V. Chernikov p.start() 511cfc9cf9bSAlexander V. Chernikov else: 512cfc9cf9bSAlexander V. Chernikov if main_vnet is not None: 513cfc9cf9bSAlexander V. Chernikov raise Exception("there can be only 1 VNET w/o handler") 514cfc9cf9bSAlexander V. Chernikov main_vnet = vnet 515cfc9cf9bSAlexander V. Chernikov # Main vnet needs to be the last, so all the other subprocesses 516cfc9cf9bSAlexander V. Chernikov # are started & their pipe handles collected 517cfc9cf9bSAlexander V. Chernikov self.vnet = main_vnet 518cfc9cf9bSAlexander V. Chernikov self._setup_vnet(main_vnet, obj_map, None) 519cfc9cf9bSAlexander V. Chernikov # Save state for the main handler 520f63825ffSAlexander V. Chernikov self.iface_map = obj_map.iface_map 521f63825ffSAlexander V. Chernikov self.vnet_map = obj_map.vnet_map 5226332ef89SAlexander V. Chernikov self.drop_privileges() 523cfc9cf9bSAlexander V. Chernikov 524cfc9cf9bSAlexander V. Chernikov def cleanup(self, test_id: str): 525cfc9cf9bSAlexander V. Chernikov # pytest test id: file::class::test_name 526f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 527cfc9cf9bSAlexander V. Chernikov 528d4a5d495SJose Luis Duran print("============= vnet cleanup =============") 529f63825ffSAlexander V. Chernikov print("# topology_id: '{}'".format(topology_id)) 530f63825ffSAlexander V. Chernikov VnetFactory(topology_id).cleanup() 531f63825ffSAlexander V. Chernikov IfaceFactory().cleanup() 532cfc9cf9bSAlexander V. Chernikov 533cfc9cf9bSAlexander V. Chernikov def wait_object(self, pipe, timeout=5): 534cfc9cf9bSAlexander V. Chernikov if pipe.poll(timeout): 535cfc9cf9bSAlexander V. Chernikov return pipe.recv() 536cfc9cf9bSAlexander V. Chernikov raise TimeoutError 537cfc9cf9bSAlexander V. Chernikov 538584ad412SAlexander V. Chernikov def wait_objects_any(self, pipe_list, timeout=5): 539584ad412SAlexander V. Chernikov objects = connection.wait(pipe_list, timeout) 540584ad412SAlexander V. Chernikov if objects: 541584ad412SAlexander V. Chernikov return objects[0].recv() 542584ad412SAlexander V. Chernikov raise TimeoutError 543584ad412SAlexander V. Chernikov 544f63825ffSAlexander V. Chernikov def send_object(self, pipe, obj): 545f63825ffSAlexander V. Chernikov pipe.send(obj) 546f63825ffSAlexander V. Chernikov 547584ad412SAlexander V. Chernikov def wait(self): 548584ad412SAlexander V. Chernikov while True: 549584ad412SAlexander V. Chernikov time.sleep(1) 550584ad412SAlexander V. Chernikov 551cfc9cf9bSAlexander V. Chernikov @property 552cfc9cf9bSAlexander V. Chernikov def curvnet(self): 553cfc9cf9bSAlexander V. Chernikov pass 554cfc9cf9bSAlexander V. Chernikov 555cfc9cf9bSAlexander V. Chernikov 556cfc9cf9bSAlexander V. Chernikovclass SingleVnetTestTemplate(VnetTestTemplate): 5578eb2bee6SAlexander V. Chernikov IPV6_PREFIXES: List[str] = [] 5588eb2bee6SAlexander V. Chernikov IPV4_PREFIXES: List[str] = [] 559f3065e76SAlexander V. Chernikov IFTYPE = "epair" 5608eb2bee6SAlexander V. Chernikov 561f3065e76SAlexander V. Chernikov def _setup_default_topology(self): 562cfc9cf9bSAlexander V. Chernikov topology = copy.deepcopy( 563cfc9cf9bSAlexander V. Chernikov { 564cfc9cf9bSAlexander V. Chernikov "vnet1": {"ifaces": ["if1"]}, 565f3065e76SAlexander V. Chernikov "if1": {"type": self.IFTYPE, "prefixes4": [], "prefixes6": []}, 566cfc9cf9bSAlexander V. Chernikov } 567cfc9cf9bSAlexander V. Chernikov ) 568cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV6_PREFIXES: 569cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes6"].append((prefix,)) 570cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV4_PREFIXES: 571cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes4"].append((prefix,)) 572f3065e76SAlexander V. Chernikov return topology 573f3065e76SAlexander V. Chernikov 574f3065e76SAlexander V. Chernikov def setup_method(self, method): 575f3065e76SAlexander V. Chernikov if not getattr(self, "TOPOLOGY", None): 576f3065e76SAlexander V. Chernikov self.TOPOLOGY = self._setup_default_topology() 577f3065e76SAlexander V. Chernikov else: 578f3065e76SAlexander V. Chernikov names = self.TOPOLOGY.keys() 579f3065e76SAlexander V. Chernikov assert len([n for n in names if n.startswith("vnet")]) == 1 580cfc9cf9bSAlexander V. Chernikov super().setup_method(method) 581