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]] = [] 608eb2bee6SAlexander V. Chernikov if iface_name.startswith("lo"): 618eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_LOOP 628eb2bee6SAlexander V. Chernikov else: 638eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_ETHER 648eb2bee6SAlexander V. Chernikov 658eb2bee6SAlexander V. Chernikov @property 668eb2bee6SAlexander V. Chernikov def ifindex(self): 678eb2bee6SAlexander V. Chernikov return socket.if_nametoindex(self.name) 688eb2bee6SAlexander V. Chernikov 69cfc9cf9bSAlexander V. Chernikov @property 70cfc9cf9bSAlexander V. Chernikov def first_ipv6(self): 71cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet6"] 72cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 73cfc9cf9bSAlexander V. Chernikov 74cfc9cf9bSAlexander V. Chernikov @property 75cfc9cf9bSAlexander V. Chernikov def first_ipv4(self): 76cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet"] 77cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 78cfc9cf9bSAlexander V. Chernikov 798eb2bee6SAlexander V. Chernikov def set_vnet(self, vnet_name: str): 808eb2bee6SAlexander V. Chernikov self.vnet_name = vnet_name 818eb2bee6SAlexander V. Chernikov 828eb2bee6SAlexander V. Chernikov def set_jailed(self, jailed: bool): 838eb2bee6SAlexander V. Chernikov self.jailed = jailed 848eb2bee6SAlexander V. Chernikov 85a1eb150cSJose Luis Duran def run_cmd(self, cmd, verbose=False): 868eb2bee6SAlexander V. Chernikov if self.vnet_name and not self.jailed: 87a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.vnet_name, cmd) 88cfc9cf9bSAlexander V. Chernikov return run_cmd(cmd, verbose) 898eb2bee6SAlexander V. Chernikov 908eb2bee6SAlexander V. Chernikov @classmethod 91cfc9cf9bSAlexander V. Chernikov def setup_loopback(cls, vnet_name: str): 92cfc9cf9bSAlexander V. Chernikov lo = VnetInterface("", "lo0") 93cfc9cf9bSAlexander V. Chernikov lo.set_vnet(vnet_name) 944856aeaaSJose Luis Duran lo.setup_addr("127.0.0.1/8") 95cfc9cf9bSAlexander V. Chernikov lo.turn_up() 96cfc9cf9bSAlexander V. Chernikov 97cfc9cf9bSAlexander V. Chernikov @classmethod 98cfc9cf9bSAlexander V. Chernikov def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: 998eb2bee6SAlexander V. Chernikov name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() 1008eb2bee6SAlexander V. Chernikov if not name: 1018eb2bee6SAlexander V. Chernikov raise Exception("Unable to create iface {}".format(iface_name)) 102cfc9cf9bSAlexander V. Chernikov ret = [cls(alias_name, name)] 1038eb2bee6SAlexander V. Chernikov if name.startswith("epair"): 104cfc9cf9bSAlexander V. Chernikov ret.append(cls(alias_name, name[:-1] + "b")) 105cfc9cf9bSAlexander V. Chernikov return ret 1068eb2bee6SAlexander V. Chernikov 107cfc9cf9bSAlexander V. Chernikov def setup_addr(self, _addr: str): 108cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_interface(_addr) 109cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 1108eb2bee6SAlexander V. Chernikov family = "inet6" 1117064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1128eb2bee6SAlexander V. Chernikov else: 1138eb2bee6SAlexander V. Chernikov family = "inet" 1147064c94aSAlexander V. Chernikov if self.addr_map[family]: 1157064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} alias {}".format(self.name, addr) 1167064c94aSAlexander V. Chernikov else: 1178eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1188eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1197064c94aSAlexander V. Chernikov self.addr_map[family][str(addr.ip)] = addr 1208eb2bee6SAlexander V. Chernikov 121cfc9cf9bSAlexander V. Chernikov def delete_addr(self, _addr: str): 122cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_address(_addr) 123cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 124cfc9cf9bSAlexander V. Chernikov family = "inet6" 1258eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6 {} delete".format(self.name, addr) 1268eb2bee6SAlexander V. Chernikov else: 127cfc9cf9bSAlexander V. Chernikov family = "inet" 1288eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} -alias {}".format(self.name, addr) 1298eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 130cfc9cf9bSAlexander V. Chernikov del self.addr_map[family][str(addr)] 1318eb2bee6SAlexander V. Chernikov 1328eb2bee6SAlexander V. Chernikov def turn_up(self): 1338eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} up".format(self.name) 1348eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1358eb2bee6SAlexander V. Chernikov 1368eb2bee6SAlexander V. Chernikov def enable_ipv6(self): 1378eb2bee6SAlexander V. Chernikov cmd = "/usr/sbin/ndp -i {} -disabled".format(self.name) 1388eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1398eb2bee6SAlexander V. Chernikov 140cfc9cf9bSAlexander V. Chernikov def has_tentative(self) -> bool: 141cfc9cf9bSAlexander V. Chernikov """True if an interface has some addresses in tenative state""" 142cfc9cf9bSAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6".format(self.name) 143cfc9cf9bSAlexander V. Chernikov out = self.run_cmd(cmd, verbose=False) 144cfc9cf9bSAlexander V. Chernikov for line in out.splitlines(): 145cfc9cf9bSAlexander V. Chernikov if "tentative" in line: 1468eb2bee6SAlexander V. Chernikov return True 1478eb2bee6SAlexander V. Chernikov return False 1488eb2bee6SAlexander V. Chernikov 1498eb2bee6SAlexander V. Chernikov 150cfc9cf9bSAlexander V. Chernikovclass IfaceFactory(object): 151cfc9cf9bSAlexander V. Chernikov INTERFACES_FNAME = "created_ifaces.lst" 152f3065e76SAlexander V. Chernikov AUTODELETE_TYPES = ("epair", "gif", "gre", "lo", "tap", "tun") 153cfc9cf9bSAlexander V. Chernikov 154f63825ffSAlexander V. Chernikov def __init__(self): 155cfc9cf9bSAlexander V. Chernikov self.file_name = self.INTERFACES_FNAME 156cfc9cf9bSAlexander V. Chernikov 157cfc9cf9bSAlexander V. Chernikov def _register_iface(self, iface_name: str): 158cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 159cfc9cf9bSAlexander V. Chernikov f.write(iface_name + "\n") 160cfc9cf9bSAlexander V. Chernikov 16120ea7f26SAlexander V. Chernikov def _list_ifaces(self) -> List[str]: 16220ea7f26SAlexander V. Chernikov ret: List[str] = [] 1638eb2bee6SAlexander V. Chernikov try: 164cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "r") as f: 1658eb2bee6SAlexander V. Chernikov for line in f: 16620ea7f26SAlexander V. Chernikov ret.append(line.strip()) 16720ea7f26SAlexander V. Chernikov except OSError: 16820ea7f26SAlexander V. Chernikov pass 16920ea7f26SAlexander V. Chernikov return ret 17020ea7f26SAlexander V. Chernikov 17120ea7f26SAlexander V. Chernikov def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: 17220ea7f26SAlexander V. Chernikov ifaces = VnetInterface.create_iface(alias_name, iface_name) 17320ea7f26SAlexander V. Chernikov for iface in ifaces: 17420ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface.name): 17520ea7f26SAlexander V. Chernikov self._register_iface(iface.name) 17620ea7f26SAlexander V. Chernikov return ifaces 17720ea7f26SAlexander V. Chernikov 17820ea7f26SAlexander V. Chernikov @staticmethod 17920ea7f26SAlexander V. Chernikov def is_autodeleted(iface_name: str) -> bool: 1802e620256SJose Luis Duran if iface_name == "lo0": 1812e620256SJose Luis Duran return False 18220ea7f26SAlexander V. Chernikov iface_type = re.split(r"\d+", iface_name)[0] 18320ea7f26SAlexander V. Chernikov return iface_type in IfaceFactory.AUTODELETE_TYPES 18420ea7f26SAlexander V. Chernikov 18520ea7f26SAlexander V. Chernikov def cleanup_vnet_interfaces(self, vnet_name: str) -> List[str]: 18620ea7f26SAlexander V. Chernikov """Destroys""" 18720ea7f26SAlexander V. Chernikov ifaces_lst = ToolsHelper.get_output( 188a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 18920ea7f26SAlexander V. Chernikov ) 19020ea7f26SAlexander V. Chernikov for iface_name in ifaces_lst.split(): 19120ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface_name): 19220ea7f26SAlexander V. Chernikov if iface_name not in self._list_ifaces(): 19320ea7f26SAlexander V. Chernikov print("Skipping interface {}:{}".format(vnet_name, iface_name)) 19420ea7f26SAlexander V. Chernikov continue 19520ea7f26SAlexander V. Chernikov run_cmd( 196a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig {} destroy".format(vnet_name, iface_name) 19720ea7f26SAlexander V. Chernikov ) 19820ea7f26SAlexander V. Chernikov 19920ea7f26SAlexander V. Chernikov def cleanup(self): 20020ea7f26SAlexander V. Chernikov try: 201cfc9cf9bSAlexander V. Chernikov os.unlink(self.INTERFACES_FNAME) 20220ea7f26SAlexander V. Chernikov except OSError: 2038eb2bee6SAlexander V. Chernikov pass 2048eb2bee6SAlexander V. Chernikov 2058eb2bee6SAlexander V. Chernikov 206cfc9cf9bSAlexander V. Chernikovclass VnetInstance(object): 207cfc9cf9bSAlexander V. Chernikov def __init__( 208cfc9cf9bSAlexander V. Chernikov self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] 209cfc9cf9bSAlexander V. Chernikov ): 210cfc9cf9bSAlexander V. Chernikov self.name = vnet_name 211cfc9cf9bSAlexander V. Chernikov self.alias = vnet_alias # reference in the test topology 212cfc9cf9bSAlexander V. Chernikov self.jid = jid 213cfc9cf9bSAlexander V. Chernikov self.ifaces = ifaces 214cfc9cf9bSAlexander V. Chernikov self.iface_alias_map = {} # iface.alias: iface 215cfc9cf9bSAlexander V. Chernikov self.iface_map = {} # iface.name: iface 2168eb2bee6SAlexander V. Chernikov for iface in ifaces: 217cfc9cf9bSAlexander V. Chernikov iface.set_vnet(vnet_name) 218cfc9cf9bSAlexander V. Chernikov iface.set_jailed(True) 219cfc9cf9bSAlexander V. Chernikov self.iface_alias_map[iface.alias] = iface 220cfc9cf9bSAlexander V. Chernikov self.iface_map[iface.name] = iface 221584ad412SAlexander V. Chernikov # Allow reference to interfce aliases as attributes 222584ad412SAlexander V. Chernikov setattr(self, iface.alias, iface) 223cfc9cf9bSAlexander V. Chernikov self.need_dad = False # Disable duplicate address detection by default 224cfc9cf9bSAlexander V. Chernikov self.attached = False 225cfc9cf9bSAlexander V. Chernikov self.pipe = None 226cfc9cf9bSAlexander V. Chernikov self.subprocess = None 227cfc9cf9bSAlexander V. Chernikov 2288a30ab53SJose Luis Duran def run_vnet_cmd(self, cmd, verbose=True): 229cfc9cf9bSAlexander V. Chernikov if not self.attached: 230a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.name, cmd) 2318a30ab53SJose Luis Duran return run_cmd(cmd, verbose) 232cfc9cf9bSAlexander V. Chernikov 233cfc9cf9bSAlexander V. Chernikov def disable_dad(self): 234cfc9cf9bSAlexander V. Chernikov self.run_vnet_cmd("/sbin/sysctl net.inet6.ip6.dad_count=0") 235cfc9cf9bSAlexander V. Chernikov 236cfc9cf9bSAlexander V. Chernikov def set_pipe(self, pipe): 237cfc9cf9bSAlexander V. Chernikov self.pipe = pipe 238cfc9cf9bSAlexander V. Chernikov 239cfc9cf9bSAlexander V. Chernikov def set_subprocess(self, p): 240cfc9cf9bSAlexander V. Chernikov self.subprocess = p 2418eb2bee6SAlexander V. Chernikov 2428eb2bee6SAlexander V. Chernikov @staticmethod 2438eb2bee6SAlexander V. Chernikov def attach_jid(jid: int): 2443873bdc2SAlexander V. Chernikov error_code = libc.jail_attach(jid) 2453873bdc2SAlexander V. Chernikov if error_code != 0: 2463873bdc2SAlexander V. Chernikov raise Exception("jail_attach() failed: errno {}".format(error_code)) 2478eb2bee6SAlexander V. Chernikov 2488eb2bee6SAlexander V. Chernikov def attach(self): 2498eb2bee6SAlexander V. Chernikov self.attach_jid(self.jid) 250cfc9cf9bSAlexander V. Chernikov self.attached = True 2518eb2bee6SAlexander V. Chernikov 2528eb2bee6SAlexander V. Chernikov 253cfc9cf9bSAlexander V. Chernikovclass VnetFactory(object): 254cfc9cf9bSAlexander V. Chernikov JAILS_FNAME = "created_jails.lst" 255cfc9cf9bSAlexander V. Chernikov 256f63825ffSAlexander V. Chernikov def __init__(self, topology_id: str): 257f63825ffSAlexander V. Chernikov self.topology_id = topology_id 258cfc9cf9bSAlexander V. Chernikov self.file_name = self.JAILS_FNAME 259cfc9cf9bSAlexander V. Chernikov self._vnets: List[str] = [] 260cfc9cf9bSAlexander V. Chernikov 261cfc9cf9bSAlexander V. Chernikov def _register_vnet(self, vnet_name: str): 262cfc9cf9bSAlexander V. Chernikov self._vnets.append(vnet_name) 263cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 264cfc9cf9bSAlexander V. Chernikov f.write(vnet_name + "\n") 265cfc9cf9bSAlexander V. Chernikov 266cfc9cf9bSAlexander V. Chernikov @staticmethod 267cfc9cf9bSAlexander V. Chernikov def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: 268a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 269cfc9cf9bSAlexander V. Chernikov not_matched: List[str] = [] 270cfc9cf9bSAlexander V. Chernikov for i in range(50): 271cfc9cf9bSAlexander V. Chernikov vnet_ifaces = run_cmd(cmd).strip().split(" ") 272cfc9cf9bSAlexander V. Chernikov not_matched = [] 273cfc9cf9bSAlexander V. Chernikov for iface_name in ifaces: 274cfc9cf9bSAlexander V. Chernikov if iface_name not in vnet_ifaces: 275cfc9cf9bSAlexander V. Chernikov not_matched.append(iface_name) 276cfc9cf9bSAlexander V. Chernikov if len(not_matched) == 0: 277cfc9cf9bSAlexander V. Chernikov return [] 278cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 279cfc9cf9bSAlexander V. Chernikov return not_matched 280cfc9cf9bSAlexander V. Chernikov 281cfc9cf9bSAlexander V. Chernikov def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]): 282f63825ffSAlexander V. Chernikov vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) 283cfc9cf9bSAlexander V. Chernikov if self._vnets: 284cfc9cf9bSAlexander V. Chernikov # add number to distinguish jails 285cfc9cf9bSAlexander V. Chernikov vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) 286cfc9cf9bSAlexander V. Chernikov iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) 287cfc9cf9bSAlexander V. Chernikov cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format( 288cfc9cf9bSAlexander V. Chernikov vnet_name, iface_cmds 289cfc9cf9bSAlexander V. Chernikov ) 290f63825ffSAlexander V. Chernikov jid = 0 291f63825ffSAlexander V. Chernikov try: 292cfc9cf9bSAlexander V. Chernikov jid_str = run_cmd(cmd) 293cfc9cf9bSAlexander V. Chernikov jid = int(jid_str) 29420ea7f26SAlexander V. Chernikov except ValueError: 295f63825ffSAlexander V. Chernikov print("Jail creation failed, output: {}".format(jid_str)) 296f63825ffSAlexander V. Chernikov raise 297cfc9cf9bSAlexander V. Chernikov self._register_vnet(vnet_name) 298cfc9cf9bSAlexander V. Chernikov 299cfc9cf9bSAlexander V. Chernikov # Run expedited version of routing 300cfc9cf9bSAlexander V. Chernikov VnetInterface.setup_loopback(vnet_name) 301cfc9cf9bSAlexander V. Chernikov 302cfc9cf9bSAlexander V. Chernikov not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) 303cfc9cf9bSAlexander V. Chernikov if not_found: 304cfc9cf9bSAlexander V. Chernikov raise Exception( 305cfc9cf9bSAlexander V. Chernikov "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) 306cfc9cf9bSAlexander V. Chernikov ) 307cfc9cf9bSAlexander V. Chernikov return VnetInstance(vnet_alias, vnet_name, jid, ifaces) 308cfc9cf9bSAlexander V. Chernikov 309cfc9cf9bSAlexander V. Chernikov def cleanup(self): 31020ea7f26SAlexander V. Chernikov iface_factory = IfaceFactory() 311cfc9cf9bSAlexander V. Chernikov try: 312cfc9cf9bSAlexander V. Chernikov with open(self.file_name) as f: 313cfc9cf9bSAlexander V. Chernikov for line in f: 314f63825ffSAlexander V. Chernikov vnet_name = line.strip() 31520ea7f26SAlexander V. Chernikov iface_factory.cleanup_vnet_interfaces(vnet_name) 316f63825ffSAlexander V. Chernikov run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) 317cfc9cf9bSAlexander V. Chernikov os.unlink(self.JAILS_FNAME) 318cfc9cf9bSAlexander V. Chernikov except OSError: 319cfc9cf9bSAlexander V. Chernikov pass 320cfc9cf9bSAlexander V. Chernikov 321cfc9cf9bSAlexander V. Chernikov 322cfc9cf9bSAlexander V. Chernikovclass SingleInterfaceMap(NamedTuple): 323cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] 324cfc9cf9bSAlexander V. Chernikov vnet_aliases: List[str] 325cfc9cf9bSAlexander V. Chernikov 326cfc9cf9bSAlexander V. Chernikov 327f63825ffSAlexander V. Chernikovclass ObjectsMap(NamedTuple): 328f63825ffSAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX 329f63825ffSAlexander V. Chernikov vnet_map: Dict[str, VnetInstance] # keyed by vnetX 330f63825ffSAlexander V. Chernikov topo_map: Dict # self.TOPOLOGY 331f63825ffSAlexander V. Chernikov 332f63825ffSAlexander V. Chernikov 3333873bdc2SAlexander V. Chernikovclass VnetTestTemplate(BaseTest): 3346332ef89SAlexander V. Chernikov NEED_ROOT: bool = True 335cfc9cf9bSAlexander V. Chernikov TOPOLOGY = {} 336cfc9cf9bSAlexander V. Chernikov 337*ae8d5881SKristof Provost def _require_default_modules(self): 338*ae8d5881SKristof Provost libc.kldload("if_epair.ko") 339*ae8d5881SKristof Provost self.require_module("if_epair") 340*ae8d5881SKristof Provost 341cfc9cf9bSAlexander V. Chernikov def _get_vnet_handler(self, vnet_alias: str): 342cfc9cf9bSAlexander V. Chernikov handler_name = "{}_handler".format(vnet_alias) 343cfc9cf9bSAlexander V. Chernikov return getattr(self, handler_name, None) 344cfc9cf9bSAlexander V. Chernikov 345cfc9cf9bSAlexander V. Chernikov def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): 346cfc9cf9bSAlexander V. Chernikov """Base Handler to setup given VNET. 347cfc9cf9bSAlexander V. Chernikov Can be run in a subprocess. If so, passes control to the special 348cfc9cf9bSAlexander V. Chernikov vnetX_handler() after setting up interface addresses 349cfc9cf9bSAlexander V. Chernikov """ 350cfc9cf9bSAlexander V. Chernikov vnet.attach() 351cfc9cf9bSAlexander V. Chernikov print("# setup_vnet({})".format(vnet.name)) 352f63825ffSAlexander V. Chernikov if pipe is not None: 353f63825ffSAlexander V. Chernikov vnet.set_pipe(pipe) 354cfc9cf9bSAlexander V. Chernikov 355f63825ffSAlexander V. Chernikov topo = obj_map.topo_map 356cfc9cf9bSAlexander V. Chernikov ipv6_ifaces = [] 357cfc9cf9bSAlexander V. Chernikov # Disable DAD 358cfc9cf9bSAlexander V. Chernikov if not vnet.need_dad: 359cfc9cf9bSAlexander V. Chernikov vnet.disable_dad() 360cfc9cf9bSAlexander V. Chernikov for iface in vnet.ifaces: 361cfc9cf9bSAlexander V. Chernikov # check index of vnet within an interface 362cfc9cf9bSAlexander V. Chernikov # as we have prefixes for both ends of the interface 363f63825ffSAlexander V. Chernikov iface_map = obj_map.iface_map[iface.alias] 364cfc9cf9bSAlexander V. Chernikov idx = iface_map.vnet_aliases.index(vnet.alias) 365cfc9cf9bSAlexander V. Chernikov prefixes6 = topo[iface.alias].get("prefixes6", []) 366cfc9cf9bSAlexander V. Chernikov prefixes4 = topo[iface.alias].get("prefixes4", []) 367cfc9cf9bSAlexander V. Chernikov if prefixes6 or prefixes4: 368cfc9cf9bSAlexander V. Chernikov ipv6_ifaces.append(iface) 369cfc9cf9bSAlexander V. Chernikov iface.turn_up() 370cfc9cf9bSAlexander V. Chernikov if prefixes6: 371cfc9cf9bSAlexander V. Chernikov iface.enable_ipv6() 372cfc9cf9bSAlexander V. Chernikov for prefix in prefixes6 + prefixes4: 373584ad412SAlexander V. Chernikov if prefix[idx]: 374cfc9cf9bSAlexander V. Chernikov iface.setup_addr(prefix[idx]) 375cfc9cf9bSAlexander V. Chernikov for iface in ipv6_ifaces: 376cfc9cf9bSAlexander V. Chernikov while iface.has_tentative(): 377cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 378cfc9cf9bSAlexander V. Chernikov 379cfc9cf9bSAlexander V. Chernikov # Run actual handler 380cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 381cfc9cf9bSAlexander V. Chernikov if handler: 382cfc9cf9bSAlexander V. Chernikov # Do unbuffered stdout for children 383cfc9cf9bSAlexander V. Chernikov # so the logs are present if the child hangs 384cfc9cf9bSAlexander V. Chernikov sys.stdout.reconfigure(line_buffering=True) 3856332ef89SAlexander V. Chernikov self.drop_privileges() 386f63825ffSAlexander V. Chernikov handler(vnet) 387cfc9cf9bSAlexander V. Chernikov 388584ad412SAlexander V. Chernikov def _get_topo_ifmap(self, topo: Dict): 389584ad412SAlexander V. Chernikov iface_factory = IfaceFactory() 390584ad412SAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] = {} 391584ad412SAlexander V. Chernikov iface_aliases = set() 392584ad412SAlexander V. Chernikov for obj_name, obj_data in topo.items(): 393584ad412SAlexander V. Chernikov if obj_name.startswith("vnet"): 394584ad412SAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 395584ad412SAlexander V. Chernikov iface_aliases.add(iface_alias) 396584ad412SAlexander V. Chernikov for iface_alias in iface_aliases: 397584ad412SAlexander V. Chernikov print("Creating {}".format(iface_alias)) 398584ad412SAlexander V. Chernikov iface_data = topo[iface_alias] 399584ad412SAlexander V. Chernikov iface_type = iface_data.get("type", "epair") 400584ad412SAlexander V. Chernikov ifaces = iface_factory.create_iface(iface_alias, iface_type) 401584ad412SAlexander V. Chernikov smap = SingleInterfaceMap(ifaces, []) 402584ad412SAlexander V. Chernikov iface_map[iface_alias] = smap 403584ad412SAlexander V. Chernikov return iface_map 404584ad412SAlexander V. Chernikov 405f63825ffSAlexander V. Chernikov def setup_topology(self, topo: Dict, topology_id: str): 406cfc9cf9bSAlexander V. Chernikov """Creates jails & interfaces for the provided topology""" 407cfc9cf9bSAlexander V. Chernikov vnet_map = {} 408f63825ffSAlexander V. Chernikov vnet_factory = VnetFactory(topology_id) 409584ad412SAlexander V. Chernikov iface_map = self._get_topo_ifmap(topo) 410cfc9cf9bSAlexander V. Chernikov for obj_name, obj_data in topo.items(): 411cfc9cf9bSAlexander V. Chernikov if obj_name.startswith("vnet"): 412cfc9cf9bSAlexander V. Chernikov vnet_ifaces = [] 413cfc9cf9bSAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 414cfc9cf9bSAlexander V. Chernikov # epair creates 2 interfaces, grab first _available_ 415cfc9cf9bSAlexander V. Chernikov # and map it to the VNET being created 416cfc9cf9bSAlexander V. Chernikov idx = len(iface_map[iface_alias].vnet_aliases) 417cfc9cf9bSAlexander V. Chernikov iface_map[iface_alias].vnet_aliases.append(obj_name) 418cfc9cf9bSAlexander V. Chernikov vnet_ifaces.append(iface_map[iface_alias].ifaces[idx]) 419cfc9cf9bSAlexander V. Chernikov vnet = vnet_factory.create_vnet(obj_name, vnet_ifaces) 420cfc9cf9bSAlexander V. Chernikov vnet_map[obj_name] = vnet 421584ad412SAlexander V. Chernikov # Allow reference to VNETs as attributes 422584ad412SAlexander V. Chernikov setattr(self, obj_name, vnet) 423cfc9cf9bSAlexander V. Chernikov # Debug output 424cfc9cf9bSAlexander V. Chernikov print("============= TEST TOPOLOGY =============") 425cfc9cf9bSAlexander V. Chernikov for vnet_alias, vnet in vnet_map.items(): 426cfc9cf9bSAlexander V. Chernikov print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") 427cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 428cfc9cf9bSAlexander V. Chernikov if handler: 429cfc9cf9bSAlexander V. Chernikov print(" handler: {}".format(handler.__name__), end="") 430cfc9cf9bSAlexander V. Chernikov print() 431cfc9cf9bSAlexander V. Chernikov for iface_alias, iface_data in iface_map.items(): 432cfc9cf9bSAlexander V. Chernikov vnets = iface_data.vnet_aliases 433cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] = iface_data.ifaces 434cfc9cf9bSAlexander V. Chernikov if len(vnets) == 1 and len(ifaces) == 2: 435cfc9cf9bSAlexander V. Chernikov print( 436cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> main::{}".format( 437cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, ifaces[1].name 438cfc9cf9bSAlexander V. Chernikov ) 439cfc9cf9bSAlexander V. Chernikov ) 440cfc9cf9bSAlexander V. Chernikov elif len(vnets) == 2 and len(ifaces) == 2: 441cfc9cf9bSAlexander V. Chernikov print( 442cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> {}::{}".format( 443cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name 444cfc9cf9bSAlexander V. Chernikov ) 445cfc9cf9bSAlexander V. Chernikov ) 446cfc9cf9bSAlexander V. Chernikov else: 447cfc9cf9bSAlexander V. Chernikov print( 448cfc9cf9bSAlexander V. Chernikov "# iface {}: ifaces: {} vnets: {}".format( 449cfc9cf9bSAlexander V. Chernikov iface_alias, vnets, [i.name for i in ifaces] 450cfc9cf9bSAlexander V. Chernikov ) 451cfc9cf9bSAlexander V. Chernikov ) 452cfc9cf9bSAlexander V. Chernikov print() 453f63825ffSAlexander V. Chernikov return ObjectsMap(iface_map, vnet_map, topo) 454cfc9cf9bSAlexander V. Chernikov 455f63825ffSAlexander V. Chernikov def setup_method(self, _method): 456cfc9cf9bSAlexander V. Chernikov """Sets up all the required topology and handlers for the given test""" 457f63825ffSAlexander V. Chernikov super().setup_method(_method) 458*ae8d5881SKristof Provost self._require_default_modules() 459*ae8d5881SKristof Provost 460f63825ffSAlexander V. Chernikov # TestIP6Output.test_output6_pktinfo[ipandif] 461f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 462cfc9cf9bSAlexander V. Chernikov topology = self.TOPOLOGY 463cfc9cf9bSAlexander V. Chernikov # First, setup kernel objects - interfaces & vnets 464f63825ffSAlexander V. Chernikov obj_map = self.setup_topology(topology, topology_id) 465cfc9cf9bSAlexander V. Chernikov main_vnet = None # one without subprocess handler 466f63825ffSAlexander V. Chernikov for vnet_alias, vnet in obj_map.vnet_map.items(): 467cfc9cf9bSAlexander V. Chernikov if self._get_vnet_handler(vnet_alias): 468cfc9cf9bSAlexander V. Chernikov # Need subprocess to run 469cfc9cf9bSAlexander V. Chernikov parent_pipe, child_pipe = Pipe() 470cfc9cf9bSAlexander V. Chernikov p = Process( 471cfc9cf9bSAlexander V. Chernikov target=self._setup_vnet, 472cfc9cf9bSAlexander V. Chernikov args=( 473cfc9cf9bSAlexander V. Chernikov vnet, 474cfc9cf9bSAlexander V. Chernikov obj_map, 475cfc9cf9bSAlexander V. Chernikov child_pipe, 476cfc9cf9bSAlexander V. Chernikov ), 477cfc9cf9bSAlexander V. Chernikov ) 478cfc9cf9bSAlexander V. Chernikov vnet.set_pipe(parent_pipe) 479cfc9cf9bSAlexander V. Chernikov vnet.set_subprocess(p) 480cfc9cf9bSAlexander V. Chernikov p.start() 481cfc9cf9bSAlexander V. Chernikov else: 482cfc9cf9bSAlexander V. Chernikov if main_vnet is not None: 483cfc9cf9bSAlexander V. Chernikov raise Exception("there can be only 1 VNET w/o handler") 484cfc9cf9bSAlexander V. Chernikov main_vnet = vnet 485cfc9cf9bSAlexander V. Chernikov # Main vnet needs to be the last, so all the other subprocesses 486cfc9cf9bSAlexander V. Chernikov # are started & their pipe handles collected 487cfc9cf9bSAlexander V. Chernikov self.vnet = main_vnet 488cfc9cf9bSAlexander V. Chernikov self._setup_vnet(main_vnet, obj_map, None) 489cfc9cf9bSAlexander V. Chernikov # Save state for the main handler 490f63825ffSAlexander V. Chernikov self.iface_map = obj_map.iface_map 491f63825ffSAlexander V. Chernikov self.vnet_map = obj_map.vnet_map 4926332ef89SAlexander V. Chernikov self.drop_privileges() 493cfc9cf9bSAlexander V. Chernikov 494cfc9cf9bSAlexander V. Chernikov def cleanup(self, test_id: str): 495cfc9cf9bSAlexander V. Chernikov # pytest test id: file::class::test_name 496f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 497cfc9cf9bSAlexander V. Chernikov 498d4a5d495SJose Luis Duran print("============= vnet cleanup =============") 499f63825ffSAlexander V. Chernikov print("# topology_id: '{}'".format(topology_id)) 500f63825ffSAlexander V. Chernikov VnetFactory(topology_id).cleanup() 501f63825ffSAlexander V. Chernikov IfaceFactory().cleanup() 502cfc9cf9bSAlexander V. Chernikov 503cfc9cf9bSAlexander V. Chernikov def wait_object(self, pipe, timeout=5): 504cfc9cf9bSAlexander V. Chernikov if pipe.poll(timeout): 505cfc9cf9bSAlexander V. Chernikov return pipe.recv() 506cfc9cf9bSAlexander V. Chernikov raise TimeoutError 507cfc9cf9bSAlexander V. Chernikov 508584ad412SAlexander V. Chernikov def wait_objects_any(self, pipe_list, timeout=5): 509584ad412SAlexander V. Chernikov objects = connection.wait(pipe_list, timeout) 510584ad412SAlexander V. Chernikov if objects: 511584ad412SAlexander V. Chernikov return objects[0].recv() 512584ad412SAlexander V. Chernikov raise TimeoutError 513584ad412SAlexander V. Chernikov 514f63825ffSAlexander V. Chernikov def send_object(self, pipe, obj): 515f63825ffSAlexander V. Chernikov pipe.send(obj) 516f63825ffSAlexander V. Chernikov 517584ad412SAlexander V. Chernikov def wait(self): 518584ad412SAlexander V. Chernikov while True: 519584ad412SAlexander V. Chernikov time.sleep(1) 520584ad412SAlexander V. Chernikov 521cfc9cf9bSAlexander V. Chernikov @property 522cfc9cf9bSAlexander V. Chernikov def curvnet(self): 523cfc9cf9bSAlexander V. Chernikov pass 524cfc9cf9bSAlexander V. Chernikov 525cfc9cf9bSAlexander V. Chernikov 526cfc9cf9bSAlexander V. Chernikovclass SingleVnetTestTemplate(VnetTestTemplate): 5278eb2bee6SAlexander V. Chernikov IPV6_PREFIXES: List[str] = [] 5288eb2bee6SAlexander V. Chernikov IPV4_PREFIXES: List[str] = [] 529f3065e76SAlexander V. Chernikov IFTYPE = "epair" 5308eb2bee6SAlexander V. Chernikov 531f3065e76SAlexander V. Chernikov def _setup_default_topology(self): 532cfc9cf9bSAlexander V. Chernikov topology = copy.deepcopy( 533cfc9cf9bSAlexander V. Chernikov { 534cfc9cf9bSAlexander V. Chernikov "vnet1": {"ifaces": ["if1"]}, 535f3065e76SAlexander V. Chernikov "if1": {"type": self.IFTYPE, "prefixes4": [], "prefixes6": []}, 536cfc9cf9bSAlexander V. Chernikov } 537cfc9cf9bSAlexander V. Chernikov ) 538cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV6_PREFIXES: 539cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes6"].append((prefix,)) 540cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV4_PREFIXES: 541cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes4"].append((prefix,)) 542f3065e76SAlexander V. Chernikov return topology 543f3065e76SAlexander V. Chernikov 544f3065e76SAlexander V. Chernikov def setup_method(self, method): 545f3065e76SAlexander V. Chernikov if not getattr(self, "TOPOLOGY", None): 546f3065e76SAlexander V. Chernikov self.TOPOLOGY = self._setup_default_topology() 547f3065e76SAlexander V. Chernikov else: 548f3065e76SAlexander V. Chernikov names = self.TOPOLOGY.keys() 549f3065e76SAlexander V. Chernikov assert len([n for n in names if n.startswith("vnet")]) == 1 550cfc9cf9bSAlexander V. Chernikov super().setup_method(method) 551