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: 228eb2bee6SAlexander V. Chernikov print("run: '{}'".format(cmd)) 238eb2bee6SAlexander V. Chernikov return os.popen(cmd).read() 248eb2bee6SAlexander V. Chernikov 258eb2bee6SAlexander V. Chernikov 26f63825ffSAlexander V. Chernikovdef get_topology_id(test_id: str) -> str: 27f63825ffSAlexander V. Chernikov """ 28f63825ffSAlexander V. Chernikov Gets a unique topology id based on the pytest test_id. 29f63825ffSAlexander V. Chernikov "test_ip6_output.py::TestIP6Output::test_output6_pktinfo[ipandif]" -> 30f63825ffSAlexander V. Chernikov "TestIP6Output:test_output6_pktinfo[ipandif]" 31f63825ffSAlexander V. Chernikov """ 32f63825ffSAlexander V. Chernikov return ":".join(test_id.split("::")[-2:]) 33f63825ffSAlexander V. Chernikov 34f63825ffSAlexander V. Chernikov 35cfc9cf9bSAlexander V. Chernikovdef convert_test_name(test_name: str) -> str: 36cfc9cf9bSAlexander V. Chernikov """Convert test name to a string that can be used in the file/jail names""" 37cfc9cf9bSAlexander V. Chernikov ret = "" 38cfc9cf9bSAlexander V. Chernikov for char in test_name: 39f63825ffSAlexander V. Chernikov if char.isalnum() or char in ("_", "-", ":"): 40cfc9cf9bSAlexander V. Chernikov ret += char 41cfc9cf9bSAlexander V. Chernikov elif char in ("["): 42cfc9cf9bSAlexander V. Chernikov ret += "_" 43cfc9cf9bSAlexander V. Chernikov return ret 448eb2bee6SAlexander V. Chernikov 45cfc9cf9bSAlexander V. Chernikov 46cfc9cf9bSAlexander V. Chernikovclass VnetInterface(object): 478eb2bee6SAlexander V. Chernikov # defines from net/if_types.h 488eb2bee6SAlexander V. Chernikov IFT_LOOP = 0x18 498eb2bee6SAlexander V. Chernikov IFT_ETHER = 0x06 508eb2bee6SAlexander V. Chernikov 51cfc9cf9bSAlexander V. Chernikov def __init__(self, iface_alias: str, iface_name: str): 528eb2bee6SAlexander V. Chernikov self.name = iface_name 53cfc9cf9bSAlexander V. Chernikov self.alias = iface_alias 548eb2bee6SAlexander V. Chernikov self.vnet_name = "" 558eb2bee6SAlexander V. Chernikov self.jailed = False 56cfc9cf9bSAlexander V. Chernikov self.addr_map: Dict[str, Dict] = {"inet6": {}, "inet": {}} 57cfc9cf9bSAlexander V. Chernikov self.prefixes4: List[List[str]] = [] 58cfc9cf9bSAlexander V. Chernikov self.prefixes6: List[List[str]] = [] 598eb2bee6SAlexander V. Chernikov if iface_name.startswith("lo"): 608eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_LOOP 618eb2bee6SAlexander V. Chernikov else: 628eb2bee6SAlexander V. Chernikov self.iftype = self.IFT_ETHER 638eb2bee6SAlexander V. Chernikov 648eb2bee6SAlexander V. Chernikov @property 658eb2bee6SAlexander V. Chernikov def ifindex(self): 668eb2bee6SAlexander V. Chernikov return socket.if_nametoindex(self.name) 678eb2bee6SAlexander V. Chernikov 68cfc9cf9bSAlexander V. Chernikov @property 69cfc9cf9bSAlexander V. Chernikov def first_ipv6(self): 70cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet6"] 71cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 72cfc9cf9bSAlexander V. Chernikov 73cfc9cf9bSAlexander V. Chernikov @property 74cfc9cf9bSAlexander V. Chernikov def first_ipv4(self): 75cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet"] 76cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 77cfc9cf9bSAlexander V. Chernikov 788eb2bee6SAlexander V. Chernikov def set_vnet(self, vnet_name: str): 798eb2bee6SAlexander V. Chernikov self.vnet_name = vnet_name 808eb2bee6SAlexander V. Chernikov 818eb2bee6SAlexander V. Chernikov def set_jailed(self, jailed: bool): 828eb2bee6SAlexander V. Chernikov self.jailed = jailed 838eb2bee6SAlexander V. Chernikov 84cfc9cf9bSAlexander V. Chernikov def run_cmd( 85cfc9cf9bSAlexander V. Chernikov self, 86cfc9cf9bSAlexander V. Chernikov cmd, 87cfc9cf9bSAlexander V. Chernikov verbose=False, 88cfc9cf9bSAlexander V. Chernikov ): 898eb2bee6SAlexander V. Chernikov if self.vnet_name and not self.jailed: 908eb2bee6SAlexander V. Chernikov cmd = "jexec {} {}".format(self.vnet_name, cmd) 91cfc9cf9bSAlexander V. Chernikov return run_cmd(cmd, verbose) 928eb2bee6SAlexander V. Chernikov 938eb2bee6SAlexander V. Chernikov @classmethod 94cfc9cf9bSAlexander V. Chernikov def setup_loopback(cls, vnet_name: str): 95cfc9cf9bSAlexander V. Chernikov lo = VnetInterface("", "lo0") 96cfc9cf9bSAlexander V. Chernikov lo.set_vnet(vnet_name) 974856aeaaSJose Luis Duran lo.setup_addr("127.0.0.1/8") 98cfc9cf9bSAlexander V. Chernikov lo.turn_up() 99cfc9cf9bSAlexander V. Chernikov 100cfc9cf9bSAlexander V. Chernikov @classmethod 101cfc9cf9bSAlexander V. Chernikov def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: 1028eb2bee6SAlexander V. Chernikov name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() 1038eb2bee6SAlexander V. Chernikov if not name: 1048eb2bee6SAlexander V. Chernikov raise Exception("Unable to create iface {}".format(iface_name)) 105cfc9cf9bSAlexander V. Chernikov ret = [cls(alias_name, name)] 1068eb2bee6SAlexander V. Chernikov if name.startswith("epair"): 107cfc9cf9bSAlexander V. Chernikov ret.append(cls(alias_name, name[:-1] + "b")) 108cfc9cf9bSAlexander V. Chernikov return ret 1098eb2bee6SAlexander V. Chernikov 110cfc9cf9bSAlexander V. Chernikov def setup_addr(self, _addr: str): 111cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_interface(_addr) 112cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 1138eb2bee6SAlexander V. Chernikov family = "inet6" 1147064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1158eb2bee6SAlexander V. Chernikov else: 1168eb2bee6SAlexander V. Chernikov family = "inet" 1177064c94aSAlexander V. Chernikov if self.addr_map[family]: 1187064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} alias {}".format(self.name, addr) 1197064c94aSAlexander V. Chernikov else: 1208eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1218eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1227064c94aSAlexander V. Chernikov self.addr_map[family][str(addr.ip)] = addr 1238eb2bee6SAlexander V. Chernikov 124cfc9cf9bSAlexander V. Chernikov def delete_addr(self, _addr: str): 125cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_address(_addr) 126cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 127cfc9cf9bSAlexander V. Chernikov family = "inet6" 1288eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6 {} delete".format(self.name, addr) 1298eb2bee6SAlexander V. Chernikov else: 130cfc9cf9bSAlexander V. Chernikov family = "inet" 1318eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} -alias {}".format(self.name, addr) 1328eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 133cfc9cf9bSAlexander V. Chernikov del self.addr_map[family][str(addr)] 1348eb2bee6SAlexander V. Chernikov 1358eb2bee6SAlexander V. Chernikov def turn_up(self): 1368eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} up".format(self.name) 1378eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1388eb2bee6SAlexander V. Chernikov 1398eb2bee6SAlexander V. Chernikov def enable_ipv6(self): 1408eb2bee6SAlexander V. Chernikov cmd = "/usr/sbin/ndp -i {} -disabled".format(self.name) 1418eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1428eb2bee6SAlexander V. Chernikov 143cfc9cf9bSAlexander V. Chernikov def has_tentative(self) -> bool: 144cfc9cf9bSAlexander V. Chernikov """True if an interface has some addresses in tenative state""" 145cfc9cf9bSAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6".format(self.name) 146cfc9cf9bSAlexander V. Chernikov out = self.run_cmd(cmd, verbose=False) 147cfc9cf9bSAlexander V. Chernikov for line in out.splitlines(): 148cfc9cf9bSAlexander V. Chernikov if "tentative" in line: 1498eb2bee6SAlexander V. Chernikov return True 1508eb2bee6SAlexander V. Chernikov return False 1518eb2bee6SAlexander V. Chernikov 1528eb2bee6SAlexander V. Chernikov 153cfc9cf9bSAlexander V. Chernikovclass IfaceFactory(object): 154cfc9cf9bSAlexander V. Chernikov INTERFACES_FNAME = "created_ifaces.lst" 155f3065e76SAlexander V. Chernikov AUTODELETE_TYPES = ("epair", "gif", "gre", "lo", "tap", "tun") 156cfc9cf9bSAlexander V. Chernikov 157f63825ffSAlexander V. Chernikov def __init__(self): 158cfc9cf9bSAlexander V. Chernikov self.file_name = self.INTERFACES_FNAME 159cfc9cf9bSAlexander V. Chernikov 160cfc9cf9bSAlexander V. Chernikov def _register_iface(self, iface_name: str): 161cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 162cfc9cf9bSAlexander V. Chernikov f.write(iface_name + "\n") 163cfc9cf9bSAlexander V. Chernikov 16420ea7f26SAlexander V. Chernikov def _list_ifaces(self) -> List[str]: 16520ea7f26SAlexander V. Chernikov ret: List[str] = [] 1668eb2bee6SAlexander V. Chernikov try: 167cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "r") as f: 1688eb2bee6SAlexander V. Chernikov for line in f: 16920ea7f26SAlexander V. Chernikov ret.append(line.strip()) 17020ea7f26SAlexander V. Chernikov except OSError: 17120ea7f26SAlexander V. Chernikov pass 17220ea7f26SAlexander V. Chernikov return ret 17320ea7f26SAlexander V. Chernikov 17420ea7f26SAlexander V. Chernikov def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: 17520ea7f26SAlexander V. Chernikov ifaces = VnetInterface.create_iface(alias_name, iface_name) 17620ea7f26SAlexander V. Chernikov for iface in ifaces: 17720ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface.name): 17820ea7f26SAlexander V. Chernikov self._register_iface(iface.name) 17920ea7f26SAlexander V. Chernikov return ifaces 18020ea7f26SAlexander V. Chernikov 18120ea7f26SAlexander V. Chernikov @staticmethod 18220ea7f26SAlexander V. Chernikov def is_autodeleted(iface_name: str) -> bool: 18320ea7f26SAlexander V. Chernikov iface_type = re.split(r"\d+", iface_name)[0] 18420ea7f26SAlexander V. Chernikov return iface_type in IfaceFactory.AUTODELETE_TYPES 18520ea7f26SAlexander V. Chernikov 18620ea7f26SAlexander V. Chernikov def cleanup_vnet_interfaces(self, vnet_name: str) -> List[str]: 18720ea7f26SAlexander V. Chernikov """Destroys""" 18820ea7f26SAlexander V. Chernikov ifaces_lst = ToolsHelper.get_output( 18920ea7f26SAlexander V. Chernikov "/usr/sbin/jexec {} ifconfig -l".format(vnet_name) 19020ea7f26SAlexander V. Chernikov ) 19120ea7f26SAlexander V. Chernikov for iface_name in ifaces_lst.split(): 19220ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface_name): 19320ea7f26SAlexander V. Chernikov if iface_name not in self._list_ifaces(): 19420ea7f26SAlexander V. Chernikov print("Skipping interface {}:{}".format(vnet_name, iface_name)) 19520ea7f26SAlexander V. Chernikov continue 19620ea7f26SAlexander V. Chernikov run_cmd( 19720ea7f26SAlexander V. Chernikov "/usr/sbin/jexec {} ifconfig {} destroy".format(vnet_name, iface_name) 19820ea7f26SAlexander V. Chernikov ) 19920ea7f26SAlexander V. Chernikov 20020ea7f26SAlexander V. Chernikov def cleanup(self): 20120ea7f26SAlexander V. Chernikov try: 202cfc9cf9bSAlexander V. Chernikov os.unlink(self.INTERFACES_FNAME) 20320ea7f26SAlexander V. Chernikov except OSError: 2048eb2bee6SAlexander V. Chernikov pass 2058eb2bee6SAlexander V. Chernikov 2068eb2bee6SAlexander V. Chernikov 207cfc9cf9bSAlexander V. Chernikovclass VnetInstance(object): 208cfc9cf9bSAlexander V. Chernikov def __init__( 209cfc9cf9bSAlexander V. Chernikov self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] 210cfc9cf9bSAlexander V. Chernikov ): 211cfc9cf9bSAlexander V. Chernikov self.name = vnet_name 212cfc9cf9bSAlexander V. Chernikov self.alias = vnet_alias # reference in the test topology 213cfc9cf9bSAlexander V. Chernikov self.jid = jid 214cfc9cf9bSAlexander V. Chernikov self.ifaces = ifaces 215cfc9cf9bSAlexander V. Chernikov self.iface_alias_map = {} # iface.alias: iface 216cfc9cf9bSAlexander V. Chernikov self.iface_map = {} # iface.name: iface 2178eb2bee6SAlexander V. Chernikov for iface in ifaces: 218cfc9cf9bSAlexander V. Chernikov iface.set_vnet(vnet_name) 219cfc9cf9bSAlexander V. Chernikov iface.set_jailed(True) 220cfc9cf9bSAlexander V. Chernikov self.iface_alias_map[iface.alias] = iface 221cfc9cf9bSAlexander V. Chernikov self.iface_map[iface.name] = iface 222584ad412SAlexander V. Chernikov # Allow reference to interfce aliases as attributes 223584ad412SAlexander V. Chernikov setattr(self, iface.alias, iface) 224cfc9cf9bSAlexander V. Chernikov self.need_dad = False # Disable duplicate address detection by default 225cfc9cf9bSAlexander V. Chernikov self.attached = False 226cfc9cf9bSAlexander V. Chernikov self.pipe = None 227cfc9cf9bSAlexander V. Chernikov self.subprocess = None 228cfc9cf9bSAlexander V. Chernikov 229cfc9cf9bSAlexander V. Chernikov def run_vnet_cmd(self, cmd): 230cfc9cf9bSAlexander V. Chernikov if not self.attached: 231cfc9cf9bSAlexander V. Chernikov cmd = "jexec {} {}".format(self.name, cmd) 232cfc9cf9bSAlexander V. Chernikov return run_cmd(cmd) 233cfc9cf9bSAlexander V. Chernikov 234cfc9cf9bSAlexander V. Chernikov def disable_dad(self): 235cfc9cf9bSAlexander V. Chernikov self.run_vnet_cmd("/sbin/sysctl net.inet6.ip6.dad_count=0") 236cfc9cf9bSAlexander V. Chernikov 237cfc9cf9bSAlexander V. Chernikov def set_pipe(self, pipe): 238cfc9cf9bSAlexander V. Chernikov self.pipe = pipe 239cfc9cf9bSAlexander V. Chernikov 240cfc9cf9bSAlexander V. Chernikov def set_subprocess(self, p): 241cfc9cf9bSAlexander V. Chernikov self.subprocess = p 2428eb2bee6SAlexander V. Chernikov 2438eb2bee6SAlexander V. Chernikov @staticmethod 2448eb2bee6SAlexander V. Chernikov def attach_jid(jid: int): 2453873bdc2SAlexander V. Chernikov error_code = libc.jail_attach(jid) 2463873bdc2SAlexander V. Chernikov if error_code != 0: 2473873bdc2SAlexander V. Chernikov raise Exception("jail_attach() failed: errno {}".format(error_code)) 2488eb2bee6SAlexander V. Chernikov 2498eb2bee6SAlexander V. Chernikov def attach(self): 2508eb2bee6SAlexander V. Chernikov self.attach_jid(self.jid) 251cfc9cf9bSAlexander V. Chernikov self.attached = True 2528eb2bee6SAlexander V. Chernikov 2538eb2bee6SAlexander V. Chernikov 254cfc9cf9bSAlexander V. Chernikovclass VnetFactory(object): 255cfc9cf9bSAlexander V. Chernikov JAILS_FNAME = "created_jails.lst" 256cfc9cf9bSAlexander V. Chernikov 257f63825ffSAlexander V. Chernikov def __init__(self, topology_id: str): 258f63825ffSAlexander V. Chernikov self.topology_id = topology_id 259cfc9cf9bSAlexander V. Chernikov self.file_name = self.JAILS_FNAME 260cfc9cf9bSAlexander V. Chernikov self._vnets: List[str] = [] 261cfc9cf9bSAlexander V. Chernikov 262cfc9cf9bSAlexander V. Chernikov def _register_vnet(self, vnet_name: str): 263cfc9cf9bSAlexander V. Chernikov self._vnets.append(vnet_name) 264cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 265cfc9cf9bSAlexander V. Chernikov f.write(vnet_name + "\n") 266cfc9cf9bSAlexander V. Chernikov 267cfc9cf9bSAlexander V. Chernikov @staticmethod 268cfc9cf9bSAlexander V. Chernikov def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: 269cfc9cf9bSAlexander V. Chernikov cmd = "jexec {} /sbin/ifconfig -l".format(vnet_name) 270cfc9cf9bSAlexander V. Chernikov not_matched: List[str] = [] 271cfc9cf9bSAlexander V. Chernikov for i in range(50): 272cfc9cf9bSAlexander V. Chernikov vnet_ifaces = run_cmd(cmd).strip().split(" ") 273cfc9cf9bSAlexander V. Chernikov not_matched = [] 274cfc9cf9bSAlexander V. Chernikov for iface_name in ifaces: 275cfc9cf9bSAlexander V. Chernikov if iface_name not in vnet_ifaces: 276cfc9cf9bSAlexander V. Chernikov not_matched.append(iface_name) 277cfc9cf9bSAlexander V. Chernikov if len(not_matched) == 0: 278cfc9cf9bSAlexander V. Chernikov return [] 279cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 280cfc9cf9bSAlexander V. Chernikov return not_matched 281cfc9cf9bSAlexander V. Chernikov 282cfc9cf9bSAlexander V. Chernikov def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]): 283f63825ffSAlexander V. Chernikov vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) 284cfc9cf9bSAlexander V. Chernikov if self._vnets: 285cfc9cf9bSAlexander V. Chernikov # add number to distinguish jails 286cfc9cf9bSAlexander V. Chernikov vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) 287cfc9cf9bSAlexander V. Chernikov iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) 288cfc9cf9bSAlexander V. Chernikov cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format( 289cfc9cf9bSAlexander V. Chernikov vnet_name, iface_cmds 290cfc9cf9bSAlexander V. Chernikov ) 291f63825ffSAlexander V. Chernikov jid = 0 292f63825ffSAlexander V. Chernikov try: 293cfc9cf9bSAlexander V. Chernikov jid_str = run_cmd(cmd) 294cfc9cf9bSAlexander V. Chernikov jid = int(jid_str) 29520ea7f26SAlexander V. Chernikov except ValueError: 296f63825ffSAlexander V. Chernikov print("Jail creation failed, output: {}".format(jid_str)) 297f63825ffSAlexander V. Chernikov raise 298cfc9cf9bSAlexander V. Chernikov self._register_vnet(vnet_name) 299cfc9cf9bSAlexander V. Chernikov 300cfc9cf9bSAlexander V. Chernikov # Run expedited version of routing 301cfc9cf9bSAlexander V. Chernikov VnetInterface.setup_loopback(vnet_name) 302cfc9cf9bSAlexander V. Chernikov 303cfc9cf9bSAlexander V. Chernikov not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) 304cfc9cf9bSAlexander V. Chernikov if not_found: 305cfc9cf9bSAlexander V. Chernikov raise Exception( 306cfc9cf9bSAlexander V. Chernikov "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) 307cfc9cf9bSAlexander V. Chernikov ) 308cfc9cf9bSAlexander V. Chernikov return VnetInstance(vnet_alias, vnet_name, jid, ifaces) 309cfc9cf9bSAlexander V. Chernikov 310cfc9cf9bSAlexander V. Chernikov def cleanup(self): 31120ea7f26SAlexander V. Chernikov iface_factory = IfaceFactory() 312cfc9cf9bSAlexander V. Chernikov try: 313cfc9cf9bSAlexander V. Chernikov with open(self.file_name) as f: 314cfc9cf9bSAlexander V. Chernikov for line in f: 315f63825ffSAlexander V. Chernikov vnet_name = line.strip() 31620ea7f26SAlexander V. Chernikov iface_factory.cleanup_vnet_interfaces(vnet_name) 317f63825ffSAlexander V. Chernikov run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) 318cfc9cf9bSAlexander V. Chernikov os.unlink(self.JAILS_FNAME) 319cfc9cf9bSAlexander V. Chernikov except OSError: 320cfc9cf9bSAlexander V. Chernikov pass 321cfc9cf9bSAlexander V. Chernikov 322cfc9cf9bSAlexander V. Chernikov 323cfc9cf9bSAlexander V. Chernikovclass SingleInterfaceMap(NamedTuple): 324cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] 325cfc9cf9bSAlexander V. Chernikov vnet_aliases: List[str] 326cfc9cf9bSAlexander V. Chernikov 327cfc9cf9bSAlexander V. Chernikov 328f63825ffSAlexander V. Chernikovclass ObjectsMap(NamedTuple): 329f63825ffSAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX 330f63825ffSAlexander V. Chernikov vnet_map: Dict[str, VnetInstance] # keyed by vnetX 331f63825ffSAlexander V. Chernikov topo_map: Dict # self.TOPOLOGY 332f63825ffSAlexander V. Chernikov 333f63825ffSAlexander V. Chernikov 3343873bdc2SAlexander V. Chernikovclass VnetTestTemplate(BaseTest): 3356332ef89SAlexander V. Chernikov NEED_ROOT: bool = True 336cfc9cf9bSAlexander V. Chernikov TOPOLOGY = {} 337cfc9cf9bSAlexander V. Chernikov 338*09d61b28SKristof Provost def _require_default_modules(self): 339*09d61b28SKristof Provost libc.kldload("if_epair.ko") 340*09d61b28SKristof Provost self.require_module("if_epair") 341*09d61b28SKristof Provost 342cfc9cf9bSAlexander V. Chernikov def _get_vnet_handler(self, vnet_alias: str): 343cfc9cf9bSAlexander V. Chernikov handler_name = "{}_handler".format(vnet_alias) 344cfc9cf9bSAlexander V. Chernikov return getattr(self, handler_name, None) 345cfc9cf9bSAlexander V. Chernikov 346cfc9cf9bSAlexander V. Chernikov def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): 347cfc9cf9bSAlexander V. Chernikov """Base Handler to setup given VNET. 348cfc9cf9bSAlexander V. Chernikov Can be run in a subprocess. If so, passes control to the special 349cfc9cf9bSAlexander V. Chernikov vnetX_handler() after setting up interface addresses 350cfc9cf9bSAlexander V. Chernikov """ 351cfc9cf9bSAlexander V. Chernikov vnet.attach() 352cfc9cf9bSAlexander V. Chernikov print("# setup_vnet({})".format(vnet.name)) 353f63825ffSAlexander V. Chernikov if pipe is not None: 354f63825ffSAlexander V. Chernikov vnet.set_pipe(pipe) 355cfc9cf9bSAlexander V. Chernikov 356f63825ffSAlexander V. Chernikov topo = obj_map.topo_map 357cfc9cf9bSAlexander V. Chernikov ipv6_ifaces = [] 358cfc9cf9bSAlexander V. Chernikov # Disable DAD 359cfc9cf9bSAlexander V. Chernikov if not vnet.need_dad: 360cfc9cf9bSAlexander V. Chernikov vnet.disable_dad() 361cfc9cf9bSAlexander V. Chernikov for iface in vnet.ifaces: 362cfc9cf9bSAlexander V. Chernikov # check index of vnet within an interface 363cfc9cf9bSAlexander V. Chernikov # as we have prefixes for both ends of the interface 364f63825ffSAlexander V. Chernikov iface_map = obj_map.iface_map[iface.alias] 365cfc9cf9bSAlexander V. Chernikov idx = iface_map.vnet_aliases.index(vnet.alias) 366cfc9cf9bSAlexander V. Chernikov prefixes6 = topo[iface.alias].get("prefixes6", []) 367cfc9cf9bSAlexander V. Chernikov prefixes4 = topo[iface.alias].get("prefixes4", []) 368cfc9cf9bSAlexander V. Chernikov if prefixes6 or prefixes4: 369cfc9cf9bSAlexander V. Chernikov ipv6_ifaces.append(iface) 370cfc9cf9bSAlexander V. Chernikov iface.turn_up() 371cfc9cf9bSAlexander V. Chernikov if prefixes6: 372cfc9cf9bSAlexander V. Chernikov iface.enable_ipv6() 373cfc9cf9bSAlexander V. Chernikov for prefix in prefixes6 + prefixes4: 374584ad412SAlexander V. Chernikov if prefix[idx]: 375cfc9cf9bSAlexander V. Chernikov iface.setup_addr(prefix[idx]) 376cfc9cf9bSAlexander V. Chernikov for iface in ipv6_ifaces: 377cfc9cf9bSAlexander V. Chernikov while iface.has_tentative(): 378cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 379cfc9cf9bSAlexander V. Chernikov 380cfc9cf9bSAlexander V. Chernikov # Run actual handler 381cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 382cfc9cf9bSAlexander V. Chernikov if handler: 383cfc9cf9bSAlexander V. Chernikov # Do unbuffered stdout for children 384cfc9cf9bSAlexander V. Chernikov # so the logs are present if the child hangs 385cfc9cf9bSAlexander V. Chernikov sys.stdout.reconfigure(line_buffering=True) 3866332ef89SAlexander V. Chernikov self.drop_privileges() 387f63825ffSAlexander V. Chernikov handler(vnet) 388cfc9cf9bSAlexander V. Chernikov 389584ad412SAlexander V. Chernikov def _get_topo_ifmap(self, topo: Dict): 390584ad412SAlexander V. Chernikov iface_factory = IfaceFactory() 391584ad412SAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] = {} 392584ad412SAlexander V. Chernikov iface_aliases = set() 393584ad412SAlexander V. Chernikov for obj_name, obj_data in topo.items(): 394584ad412SAlexander V. Chernikov if obj_name.startswith("vnet"): 395584ad412SAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 396584ad412SAlexander V. Chernikov iface_aliases.add(iface_alias) 397584ad412SAlexander V. Chernikov for iface_alias in iface_aliases: 398584ad412SAlexander V. Chernikov print("Creating {}".format(iface_alias)) 399584ad412SAlexander V. Chernikov iface_data = topo[iface_alias] 400584ad412SAlexander V. Chernikov iface_type = iface_data.get("type", "epair") 401584ad412SAlexander V. Chernikov ifaces = iface_factory.create_iface(iface_alias, iface_type) 402584ad412SAlexander V. Chernikov smap = SingleInterfaceMap(ifaces, []) 403584ad412SAlexander V. Chernikov iface_map[iface_alias] = smap 404584ad412SAlexander V. Chernikov return iface_map 405584ad412SAlexander V. Chernikov 406f63825ffSAlexander V. Chernikov def setup_topology(self, topo: Dict, topology_id: str): 407cfc9cf9bSAlexander V. Chernikov """Creates jails & interfaces for the provided topology""" 408cfc9cf9bSAlexander V. Chernikov vnet_map = {} 409f63825ffSAlexander V. Chernikov vnet_factory = VnetFactory(topology_id) 410584ad412SAlexander V. Chernikov iface_map = self._get_topo_ifmap(topo) 411cfc9cf9bSAlexander V. Chernikov for obj_name, obj_data in topo.items(): 412cfc9cf9bSAlexander V. Chernikov if obj_name.startswith("vnet"): 413cfc9cf9bSAlexander V. Chernikov vnet_ifaces = [] 414cfc9cf9bSAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 415cfc9cf9bSAlexander V. Chernikov # epair creates 2 interfaces, grab first _available_ 416cfc9cf9bSAlexander V. Chernikov # and map it to the VNET being created 417cfc9cf9bSAlexander V. Chernikov idx = len(iface_map[iface_alias].vnet_aliases) 418cfc9cf9bSAlexander V. Chernikov iface_map[iface_alias].vnet_aliases.append(obj_name) 419cfc9cf9bSAlexander V. Chernikov vnet_ifaces.append(iface_map[iface_alias].ifaces[idx]) 420cfc9cf9bSAlexander V. Chernikov vnet = vnet_factory.create_vnet(obj_name, vnet_ifaces) 421cfc9cf9bSAlexander V. Chernikov vnet_map[obj_name] = vnet 422584ad412SAlexander V. Chernikov # Allow reference to VNETs as attributes 423584ad412SAlexander V. Chernikov setattr(self, obj_name, vnet) 424cfc9cf9bSAlexander V. Chernikov # Debug output 425cfc9cf9bSAlexander V. Chernikov print("============= TEST TOPOLOGY =============") 426cfc9cf9bSAlexander V. Chernikov for vnet_alias, vnet in vnet_map.items(): 427cfc9cf9bSAlexander V. Chernikov print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") 428cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 429cfc9cf9bSAlexander V. Chernikov if handler: 430cfc9cf9bSAlexander V. Chernikov print(" handler: {}".format(handler.__name__), end="") 431cfc9cf9bSAlexander V. Chernikov print() 432cfc9cf9bSAlexander V. Chernikov for iface_alias, iface_data in iface_map.items(): 433cfc9cf9bSAlexander V. Chernikov vnets = iface_data.vnet_aliases 434cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] = iface_data.ifaces 435cfc9cf9bSAlexander V. Chernikov if len(vnets) == 1 and len(ifaces) == 2: 436cfc9cf9bSAlexander V. Chernikov print( 437cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> main::{}".format( 438cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, ifaces[1].name 439cfc9cf9bSAlexander V. Chernikov ) 440cfc9cf9bSAlexander V. Chernikov ) 441cfc9cf9bSAlexander V. Chernikov elif len(vnets) == 2 and len(ifaces) == 2: 442cfc9cf9bSAlexander V. Chernikov print( 443cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> {}::{}".format( 444cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name 445cfc9cf9bSAlexander V. Chernikov ) 446cfc9cf9bSAlexander V. Chernikov ) 447cfc9cf9bSAlexander V. Chernikov else: 448cfc9cf9bSAlexander V. Chernikov print( 449cfc9cf9bSAlexander V. Chernikov "# iface {}: ifaces: {} vnets: {}".format( 450cfc9cf9bSAlexander V. Chernikov iface_alias, vnets, [i.name for i in ifaces] 451cfc9cf9bSAlexander V. Chernikov ) 452cfc9cf9bSAlexander V. Chernikov ) 453cfc9cf9bSAlexander V. Chernikov print() 454f63825ffSAlexander V. Chernikov return ObjectsMap(iface_map, vnet_map, topo) 455cfc9cf9bSAlexander V. Chernikov 456f63825ffSAlexander V. Chernikov def setup_method(self, _method): 457cfc9cf9bSAlexander V. Chernikov """Sets up all the required topology and handlers for the given test""" 458f63825ffSAlexander V. Chernikov super().setup_method(_method) 459*09d61b28SKristof Provost self._require_default_modules() 460*09d61b28SKristof Provost 461f63825ffSAlexander V. Chernikov # TestIP6Output.test_output6_pktinfo[ipandif] 462f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 463cfc9cf9bSAlexander V. Chernikov topology = self.TOPOLOGY 464cfc9cf9bSAlexander V. Chernikov # First, setup kernel objects - interfaces & vnets 465f63825ffSAlexander V. Chernikov obj_map = self.setup_topology(topology, topology_id) 466cfc9cf9bSAlexander V. Chernikov main_vnet = None # one without subprocess handler 467f63825ffSAlexander V. Chernikov for vnet_alias, vnet in obj_map.vnet_map.items(): 468cfc9cf9bSAlexander V. Chernikov if self._get_vnet_handler(vnet_alias): 469cfc9cf9bSAlexander V. Chernikov # Need subprocess to run 470cfc9cf9bSAlexander V. Chernikov parent_pipe, child_pipe = Pipe() 471cfc9cf9bSAlexander V. Chernikov p = Process( 472cfc9cf9bSAlexander V. Chernikov target=self._setup_vnet, 473cfc9cf9bSAlexander V. Chernikov args=( 474cfc9cf9bSAlexander V. Chernikov vnet, 475cfc9cf9bSAlexander V. Chernikov obj_map, 476cfc9cf9bSAlexander V. Chernikov child_pipe, 477cfc9cf9bSAlexander V. Chernikov ), 478cfc9cf9bSAlexander V. Chernikov ) 479cfc9cf9bSAlexander V. Chernikov vnet.set_pipe(parent_pipe) 480cfc9cf9bSAlexander V. Chernikov vnet.set_subprocess(p) 481cfc9cf9bSAlexander V. Chernikov p.start() 482cfc9cf9bSAlexander V. Chernikov else: 483cfc9cf9bSAlexander V. Chernikov if main_vnet is not None: 484cfc9cf9bSAlexander V. Chernikov raise Exception("there can be only 1 VNET w/o handler") 485cfc9cf9bSAlexander V. Chernikov main_vnet = vnet 486cfc9cf9bSAlexander V. Chernikov # Main vnet needs to be the last, so all the other subprocesses 487cfc9cf9bSAlexander V. Chernikov # are started & their pipe handles collected 488cfc9cf9bSAlexander V. Chernikov self.vnet = main_vnet 489cfc9cf9bSAlexander V. Chernikov self._setup_vnet(main_vnet, obj_map, None) 490cfc9cf9bSAlexander V. Chernikov # Save state for the main handler 491f63825ffSAlexander V. Chernikov self.iface_map = obj_map.iface_map 492f63825ffSAlexander V. Chernikov self.vnet_map = obj_map.vnet_map 4936332ef89SAlexander V. Chernikov self.drop_privileges() 494cfc9cf9bSAlexander V. Chernikov 495cfc9cf9bSAlexander V. Chernikov def cleanup(self, test_id: str): 496cfc9cf9bSAlexander V. Chernikov # pytest test id: file::class::test_name 497f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 498cfc9cf9bSAlexander V. Chernikov 499cfc9cf9bSAlexander V. Chernikov print("==== vnet cleanup ===") 500f63825ffSAlexander V. Chernikov print("# topology_id: '{}'".format(topology_id)) 501f63825ffSAlexander V. Chernikov VnetFactory(topology_id).cleanup() 502f63825ffSAlexander V. Chernikov IfaceFactory().cleanup() 503cfc9cf9bSAlexander V. Chernikov 504cfc9cf9bSAlexander V. Chernikov def wait_object(self, pipe, timeout=5): 505cfc9cf9bSAlexander V. Chernikov if pipe.poll(timeout): 506cfc9cf9bSAlexander V. Chernikov return pipe.recv() 507cfc9cf9bSAlexander V. Chernikov raise TimeoutError 508cfc9cf9bSAlexander V. Chernikov 509584ad412SAlexander V. Chernikov def wait_objects_any(self, pipe_list, timeout=5): 510584ad412SAlexander V. Chernikov objects = connection.wait(pipe_list, timeout) 511584ad412SAlexander V. Chernikov if objects: 512584ad412SAlexander V. Chernikov return objects[0].recv() 513584ad412SAlexander V. Chernikov raise TimeoutError 514584ad412SAlexander V. Chernikov 515f63825ffSAlexander V. Chernikov def send_object(self, pipe, obj): 516f63825ffSAlexander V. Chernikov pipe.send(obj) 517f63825ffSAlexander V. Chernikov 518584ad412SAlexander V. Chernikov def wait(self): 519584ad412SAlexander V. Chernikov while True: 520584ad412SAlexander V. Chernikov time.sleep(1) 521584ad412SAlexander V. Chernikov 522cfc9cf9bSAlexander V. Chernikov @property 523cfc9cf9bSAlexander V. Chernikov def curvnet(self): 524cfc9cf9bSAlexander V. Chernikov pass 525cfc9cf9bSAlexander V. Chernikov 526cfc9cf9bSAlexander V. Chernikov 527cfc9cf9bSAlexander V. Chernikovclass SingleVnetTestTemplate(VnetTestTemplate): 5288eb2bee6SAlexander V. Chernikov IPV6_PREFIXES: List[str] = [] 5298eb2bee6SAlexander V. Chernikov IPV4_PREFIXES: List[str] = [] 530f3065e76SAlexander V. Chernikov IFTYPE = "epair" 5318eb2bee6SAlexander V. Chernikov 532f3065e76SAlexander V. Chernikov def _setup_default_topology(self): 533cfc9cf9bSAlexander V. Chernikov topology = copy.deepcopy( 534cfc9cf9bSAlexander V. Chernikov { 535cfc9cf9bSAlexander V. Chernikov "vnet1": {"ifaces": ["if1"]}, 536f3065e76SAlexander V. Chernikov "if1": {"type": self.IFTYPE, "prefixes4": [], "prefixes6": []}, 537cfc9cf9bSAlexander V. Chernikov } 538cfc9cf9bSAlexander V. Chernikov ) 539cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV6_PREFIXES: 540cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes6"].append((prefix,)) 541cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV4_PREFIXES: 542cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes4"].append((prefix,)) 543f3065e76SAlexander V. Chernikov return topology 544f3065e76SAlexander V. Chernikov 545f3065e76SAlexander V. Chernikov def setup_method(self, method): 546f3065e76SAlexander V. Chernikov if not getattr(self, "TOPOLOGY", None): 547f3065e76SAlexander V. Chernikov self.TOPOLOGY = self._setup_default_topology() 548f3065e76SAlexander V. Chernikov else: 549f3065e76SAlexander V. Chernikov names = self.TOPOLOGY.keys() 550f3065e76SAlexander V. Chernikov assert len([n for n in names if n.startswith("vnet")]) == 1 551cfc9cf9bSAlexander V. Chernikov super().setup_method(method) 552