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 64*9c95fcb7SRonald Klop self.ether = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % iface_name).rstrip() 658eb2bee6SAlexander V. Chernikov 668eb2bee6SAlexander V. Chernikov @property 678eb2bee6SAlexander V. Chernikov def ifindex(self): 688eb2bee6SAlexander V. Chernikov return socket.if_nametoindex(self.name) 698eb2bee6SAlexander V. Chernikov 70cfc9cf9bSAlexander V. Chernikov @property 71cfc9cf9bSAlexander V. Chernikov def first_ipv6(self): 72cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet6"] 73cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 74cfc9cf9bSAlexander V. Chernikov 75cfc9cf9bSAlexander V. Chernikov @property 76cfc9cf9bSAlexander V. Chernikov def first_ipv4(self): 77cfc9cf9bSAlexander V. Chernikov d = self.addr_map["inet"] 78cfc9cf9bSAlexander V. Chernikov return d[next(iter(d))] 79cfc9cf9bSAlexander V. Chernikov 808eb2bee6SAlexander V. Chernikov def set_vnet(self, vnet_name: str): 818eb2bee6SAlexander V. Chernikov self.vnet_name = vnet_name 828eb2bee6SAlexander V. Chernikov 838eb2bee6SAlexander V. Chernikov def set_jailed(self, jailed: bool): 848eb2bee6SAlexander V. Chernikov self.jailed = jailed 858eb2bee6SAlexander V. Chernikov 86a1eb150cSJose Luis Duran def run_cmd(self, cmd, verbose=False): 878eb2bee6SAlexander V. Chernikov if self.vnet_name and not self.jailed: 88a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.vnet_name, cmd) 89cfc9cf9bSAlexander V. Chernikov return run_cmd(cmd, verbose) 908eb2bee6SAlexander V. Chernikov 918eb2bee6SAlexander V. Chernikov @classmethod 92cfc9cf9bSAlexander V. Chernikov def setup_loopback(cls, vnet_name: str): 93cfc9cf9bSAlexander V. Chernikov lo = VnetInterface("", "lo0") 94cfc9cf9bSAlexander V. Chernikov lo.set_vnet(vnet_name) 954856aeaaSJose Luis Duran lo.setup_addr("127.0.0.1/8") 96cfc9cf9bSAlexander V. Chernikov lo.turn_up() 97cfc9cf9bSAlexander V. Chernikov 98cfc9cf9bSAlexander V. Chernikov @classmethod 99cfc9cf9bSAlexander V. Chernikov def create_iface(cls, alias_name: str, iface_name: str) -> List["VnetInterface"]: 1008eb2bee6SAlexander V. Chernikov name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() 1018eb2bee6SAlexander V. Chernikov if not name: 1028eb2bee6SAlexander V. Chernikov raise Exception("Unable to create iface {}".format(iface_name)) 103*9c95fcb7SRonald Klop if1 = cls(alias_name, name) 104*9c95fcb7SRonald Klop ret = [if1] 1058eb2bee6SAlexander V. Chernikov if name.startswith("epair"): 106*9c95fcb7SRonald Klop if2 = cls(alias_name, name[:-1] + "b") 107*9c95fcb7SRonald Klop if1.epairb = if2 108*9c95fcb7SRonald Klop ret.append(if2); 109cfc9cf9bSAlexander V. Chernikov return ret 1108eb2bee6SAlexander V. Chernikov 111cfc9cf9bSAlexander V. Chernikov def setup_addr(self, _addr: str): 112cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_interface(_addr) 113cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 1148eb2bee6SAlexander V. Chernikov family = "inet6" 1157064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1168eb2bee6SAlexander V. Chernikov else: 1178eb2bee6SAlexander V. Chernikov family = "inet" 1187064c94aSAlexander V. Chernikov if self.addr_map[family]: 1197064c94aSAlexander V. Chernikov cmd = "/sbin/ifconfig {} alias {}".format(self.name, addr) 1207064c94aSAlexander V. Chernikov else: 1218eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} {} {}".format(self.name, family, addr) 1228eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1237064c94aSAlexander V. Chernikov self.addr_map[family][str(addr.ip)] = addr 1248eb2bee6SAlexander V. Chernikov 125cfc9cf9bSAlexander V. Chernikov def delete_addr(self, _addr: str): 126cfc9cf9bSAlexander V. Chernikov addr = ipaddress.ip_address(_addr) 127cfc9cf9bSAlexander V. Chernikov if addr.version == 6: 128cfc9cf9bSAlexander V. Chernikov family = "inet6" 1298eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6 {} delete".format(self.name, addr) 1308eb2bee6SAlexander V. Chernikov else: 131cfc9cf9bSAlexander V. Chernikov family = "inet" 1328eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} -alias {}".format(self.name, addr) 1338eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 134cfc9cf9bSAlexander V. Chernikov del self.addr_map[family][str(addr)] 1358eb2bee6SAlexander V. Chernikov 1368eb2bee6SAlexander V. Chernikov def turn_up(self): 1378eb2bee6SAlexander V. Chernikov cmd = "/sbin/ifconfig {} up".format(self.name) 1388eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1398eb2bee6SAlexander V. Chernikov 1408eb2bee6SAlexander V. Chernikov def enable_ipv6(self): 1416ae89b2fSKristof Provost cmd = "/usr/sbin/ndp -i {} -- -disabled".format(self.name) 1428eb2bee6SAlexander V. Chernikov self.run_cmd(cmd) 1438eb2bee6SAlexander V. Chernikov 144cfc9cf9bSAlexander V. Chernikov def has_tentative(self) -> bool: 145cfc9cf9bSAlexander V. Chernikov """True if an interface has some addresses in tenative state""" 146cfc9cf9bSAlexander V. Chernikov cmd = "/sbin/ifconfig {} inet6".format(self.name) 147cfc9cf9bSAlexander V. Chernikov out = self.run_cmd(cmd, verbose=False) 148cfc9cf9bSAlexander V. Chernikov for line in out.splitlines(): 149cfc9cf9bSAlexander V. Chernikov if "tentative" in line: 1508eb2bee6SAlexander V. Chernikov return True 1518eb2bee6SAlexander V. Chernikov return False 1528eb2bee6SAlexander V. Chernikov 1538eb2bee6SAlexander V. Chernikov 154cfc9cf9bSAlexander V. Chernikovclass IfaceFactory(object): 155cfc9cf9bSAlexander V. Chernikov INTERFACES_FNAME = "created_ifaces.lst" 156f3065e76SAlexander V. Chernikov AUTODELETE_TYPES = ("epair", "gif", "gre", "lo", "tap", "tun") 157cfc9cf9bSAlexander V. Chernikov 158f63825ffSAlexander V. Chernikov def __init__(self): 159cfc9cf9bSAlexander V. Chernikov self.file_name = self.INTERFACES_FNAME 160cfc9cf9bSAlexander V. Chernikov 161cfc9cf9bSAlexander V. Chernikov def _register_iface(self, iface_name: str): 162cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 163cfc9cf9bSAlexander V. Chernikov f.write(iface_name + "\n") 164cfc9cf9bSAlexander V. Chernikov 16520ea7f26SAlexander V. Chernikov def _list_ifaces(self) -> List[str]: 16620ea7f26SAlexander V. Chernikov ret: List[str] = [] 1678eb2bee6SAlexander V. Chernikov try: 168cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "r") as f: 1698eb2bee6SAlexander V. Chernikov for line in f: 17020ea7f26SAlexander V. Chernikov ret.append(line.strip()) 17120ea7f26SAlexander V. Chernikov except OSError: 17220ea7f26SAlexander V. Chernikov pass 17320ea7f26SAlexander V. Chernikov return ret 17420ea7f26SAlexander V. Chernikov 17520ea7f26SAlexander V. Chernikov def create_iface(self, alias_name: str, iface_name: str) -> List[VnetInterface]: 17620ea7f26SAlexander V. Chernikov ifaces = VnetInterface.create_iface(alias_name, iface_name) 17720ea7f26SAlexander V. Chernikov for iface in ifaces: 17820ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface.name): 17920ea7f26SAlexander V. Chernikov self._register_iface(iface.name) 18020ea7f26SAlexander V. Chernikov return ifaces 18120ea7f26SAlexander V. Chernikov 18220ea7f26SAlexander V. Chernikov @staticmethod 18320ea7f26SAlexander V. Chernikov def is_autodeleted(iface_name: str) -> bool: 1842e620256SJose Luis Duran if iface_name == "lo0": 1852e620256SJose Luis Duran return False 18620ea7f26SAlexander V. Chernikov iface_type = re.split(r"\d+", iface_name)[0] 18720ea7f26SAlexander V. Chernikov return iface_type in IfaceFactory.AUTODELETE_TYPES 18820ea7f26SAlexander V. Chernikov 18920ea7f26SAlexander V. Chernikov def cleanup_vnet_interfaces(self, vnet_name: str) -> List[str]: 19020ea7f26SAlexander V. Chernikov """Destroys""" 19120ea7f26SAlexander V. Chernikov ifaces_lst = ToolsHelper.get_output( 192a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 19320ea7f26SAlexander V. Chernikov ) 19420ea7f26SAlexander V. Chernikov for iface_name in ifaces_lst.split(): 19520ea7f26SAlexander V. Chernikov if not self.is_autodeleted(iface_name): 19620ea7f26SAlexander V. Chernikov if iface_name not in self._list_ifaces(): 19720ea7f26SAlexander V. Chernikov print("Skipping interface {}:{}".format(vnet_name, iface_name)) 19820ea7f26SAlexander V. Chernikov continue 19920ea7f26SAlexander V. Chernikov run_cmd( 200a1eb150cSJose Luis Duran "/usr/sbin/jexec {} /sbin/ifconfig {} destroy".format(vnet_name, iface_name) 20120ea7f26SAlexander V. Chernikov ) 20220ea7f26SAlexander V. Chernikov 20320ea7f26SAlexander V. Chernikov def cleanup(self): 20420ea7f26SAlexander V. Chernikov try: 205cfc9cf9bSAlexander V. Chernikov os.unlink(self.INTERFACES_FNAME) 20620ea7f26SAlexander V. Chernikov except OSError: 2078eb2bee6SAlexander V. Chernikov pass 2088eb2bee6SAlexander V. Chernikov 2098eb2bee6SAlexander V. Chernikov 210cfc9cf9bSAlexander V. Chernikovclass VnetInstance(object): 211cfc9cf9bSAlexander V. Chernikov def __init__( 212cfc9cf9bSAlexander V. Chernikov self, vnet_alias: str, vnet_name: str, jid: int, ifaces: List[VnetInterface] 213cfc9cf9bSAlexander V. Chernikov ): 214cfc9cf9bSAlexander V. Chernikov self.name = vnet_name 215cfc9cf9bSAlexander V. Chernikov self.alias = vnet_alias # reference in the test topology 216cfc9cf9bSAlexander V. Chernikov self.jid = jid 217cfc9cf9bSAlexander V. Chernikov self.ifaces = ifaces 218cfc9cf9bSAlexander V. Chernikov self.iface_alias_map = {} # iface.alias: iface 219cfc9cf9bSAlexander V. Chernikov self.iface_map = {} # iface.name: iface 2208eb2bee6SAlexander V. Chernikov for iface in ifaces: 221cfc9cf9bSAlexander V. Chernikov iface.set_vnet(vnet_name) 222cfc9cf9bSAlexander V. Chernikov iface.set_jailed(True) 223cfc9cf9bSAlexander V. Chernikov self.iface_alias_map[iface.alias] = iface 224cfc9cf9bSAlexander V. Chernikov self.iface_map[iface.name] = iface 225584ad412SAlexander V. Chernikov # Allow reference to interfce aliases as attributes 226584ad412SAlexander V. Chernikov setattr(self, iface.alias, iface) 227cfc9cf9bSAlexander V. Chernikov self.need_dad = False # Disable duplicate address detection by default 228cfc9cf9bSAlexander V. Chernikov self.attached = False 229cfc9cf9bSAlexander V. Chernikov self.pipe = None 230cfc9cf9bSAlexander V. Chernikov self.subprocess = None 231cfc9cf9bSAlexander V. Chernikov 2328a30ab53SJose Luis Duran def run_vnet_cmd(self, cmd, verbose=True): 233cfc9cf9bSAlexander V. Chernikov if not self.attached: 234a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} {}".format(self.name, cmd) 2358a30ab53SJose Luis Duran return run_cmd(cmd, verbose) 236cfc9cf9bSAlexander V. Chernikov 237cfc9cf9bSAlexander V. Chernikov def disable_dad(self): 238cfc9cf9bSAlexander V. Chernikov self.run_vnet_cmd("/sbin/sysctl net.inet6.ip6.dad_count=0") 239cfc9cf9bSAlexander V. Chernikov 240cfc9cf9bSAlexander V. Chernikov def set_pipe(self, pipe): 241cfc9cf9bSAlexander V. Chernikov self.pipe = pipe 242cfc9cf9bSAlexander V. Chernikov 243cfc9cf9bSAlexander V. Chernikov def set_subprocess(self, p): 244cfc9cf9bSAlexander V. Chernikov self.subprocess = p 2458eb2bee6SAlexander V. Chernikov 2468eb2bee6SAlexander V. Chernikov @staticmethod 2478eb2bee6SAlexander V. Chernikov def attach_jid(jid: int): 2483873bdc2SAlexander V. Chernikov error_code = libc.jail_attach(jid) 2493873bdc2SAlexander V. Chernikov if error_code != 0: 2503873bdc2SAlexander V. Chernikov raise Exception("jail_attach() failed: errno {}".format(error_code)) 2518eb2bee6SAlexander V. Chernikov 2528eb2bee6SAlexander V. Chernikov def attach(self): 2538eb2bee6SAlexander V. Chernikov self.attach_jid(self.jid) 254cfc9cf9bSAlexander V. Chernikov self.attached = True 2558eb2bee6SAlexander V. Chernikov 2568eb2bee6SAlexander V. Chernikov 257cfc9cf9bSAlexander V. Chernikovclass VnetFactory(object): 258cfc9cf9bSAlexander V. Chernikov JAILS_FNAME = "created_jails.lst" 259cfc9cf9bSAlexander V. Chernikov 260f63825ffSAlexander V. Chernikov def __init__(self, topology_id: str): 261f63825ffSAlexander V. Chernikov self.topology_id = topology_id 262cfc9cf9bSAlexander V. Chernikov self.file_name = self.JAILS_FNAME 263cfc9cf9bSAlexander V. Chernikov self._vnets: List[str] = [] 264cfc9cf9bSAlexander V. Chernikov 265cfc9cf9bSAlexander V. Chernikov def _register_vnet(self, vnet_name: str): 266cfc9cf9bSAlexander V. Chernikov self._vnets.append(vnet_name) 267cfc9cf9bSAlexander V. Chernikov with open(self.file_name, "a") as f: 268cfc9cf9bSAlexander V. Chernikov f.write(vnet_name + "\n") 269cfc9cf9bSAlexander V. Chernikov 270cfc9cf9bSAlexander V. Chernikov @staticmethod 271cfc9cf9bSAlexander V. Chernikov def _wait_interfaces(vnet_name: str, ifaces: List[str]) -> List[str]: 272a1eb150cSJose Luis Duran cmd = "/usr/sbin/jexec {} /sbin/ifconfig -l".format(vnet_name) 273cfc9cf9bSAlexander V. Chernikov not_matched: List[str] = [] 274cfc9cf9bSAlexander V. Chernikov for i in range(50): 275cfc9cf9bSAlexander V. Chernikov vnet_ifaces = run_cmd(cmd).strip().split(" ") 276cfc9cf9bSAlexander V. Chernikov not_matched = [] 277cfc9cf9bSAlexander V. Chernikov for iface_name in ifaces: 278cfc9cf9bSAlexander V. Chernikov if iface_name not in vnet_ifaces: 279cfc9cf9bSAlexander V. Chernikov not_matched.append(iface_name) 280cfc9cf9bSAlexander V. Chernikov if len(not_matched) == 0: 281cfc9cf9bSAlexander V. Chernikov return [] 282cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 283cfc9cf9bSAlexander V. Chernikov return not_matched 284cfc9cf9bSAlexander V. Chernikov 285cfc9cf9bSAlexander V. Chernikov def create_vnet(self, vnet_alias: str, ifaces: List[VnetInterface]): 286f63825ffSAlexander V. Chernikov vnet_name = "pytest:{}".format(convert_test_name(self.topology_id)) 287cfc9cf9bSAlexander V. Chernikov if self._vnets: 288cfc9cf9bSAlexander V. Chernikov # add number to distinguish jails 289cfc9cf9bSAlexander V. Chernikov vnet_name = "{}_{}".format(vnet_name, len(self._vnets) + 1) 290cfc9cf9bSAlexander V. Chernikov iface_cmds = " ".join(["vnet.interface={}".format(i.name) for i in ifaces]) 291cfc9cf9bSAlexander V. Chernikov cmd = "/usr/sbin/jail -i -c name={} persist vnet {}".format( 292cfc9cf9bSAlexander V. Chernikov vnet_name, iface_cmds 293cfc9cf9bSAlexander V. Chernikov ) 294f63825ffSAlexander V. Chernikov jid = 0 295f63825ffSAlexander V. Chernikov try: 296cfc9cf9bSAlexander V. Chernikov jid_str = run_cmd(cmd) 297cfc9cf9bSAlexander V. Chernikov jid = int(jid_str) 29820ea7f26SAlexander V. Chernikov except ValueError: 299f63825ffSAlexander V. Chernikov print("Jail creation failed, output: {}".format(jid_str)) 300f63825ffSAlexander V. Chernikov raise 301cfc9cf9bSAlexander V. Chernikov self._register_vnet(vnet_name) 302cfc9cf9bSAlexander V. Chernikov 303cfc9cf9bSAlexander V. Chernikov # Run expedited version of routing 304cfc9cf9bSAlexander V. Chernikov VnetInterface.setup_loopback(vnet_name) 305cfc9cf9bSAlexander V. Chernikov 306cfc9cf9bSAlexander V. Chernikov not_found = self._wait_interfaces(vnet_name, [i.name for i in ifaces]) 307cfc9cf9bSAlexander V. Chernikov if not_found: 308cfc9cf9bSAlexander V. Chernikov raise Exception( 309cfc9cf9bSAlexander V. Chernikov "Interfaces {} has not appeared in vnet {}".format(not_found, vnet_name) 310cfc9cf9bSAlexander V. Chernikov ) 311cfc9cf9bSAlexander V. Chernikov return VnetInstance(vnet_alias, vnet_name, jid, ifaces) 312cfc9cf9bSAlexander V. Chernikov 313cfc9cf9bSAlexander V. Chernikov def cleanup(self): 31420ea7f26SAlexander V. Chernikov iface_factory = IfaceFactory() 315cfc9cf9bSAlexander V. Chernikov try: 316cfc9cf9bSAlexander V. Chernikov with open(self.file_name) as f: 317cfc9cf9bSAlexander V. Chernikov for line in f: 318f63825ffSAlexander V. Chernikov vnet_name = line.strip() 31920ea7f26SAlexander V. Chernikov iface_factory.cleanup_vnet_interfaces(vnet_name) 320f63825ffSAlexander V. Chernikov run_cmd("/usr/sbin/jail -r {}".format(vnet_name)) 321cfc9cf9bSAlexander V. Chernikov os.unlink(self.JAILS_FNAME) 322cfc9cf9bSAlexander V. Chernikov except OSError: 323cfc9cf9bSAlexander V. Chernikov pass 324cfc9cf9bSAlexander V. Chernikov 325cfc9cf9bSAlexander V. Chernikov 326cfc9cf9bSAlexander V. Chernikovclass SingleInterfaceMap(NamedTuple): 327cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] 328cfc9cf9bSAlexander V. Chernikov vnet_aliases: List[str] 329cfc9cf9bSAlexander V. Chernikov 330cfc9cf9bSAlexander V. Chernikov 331f63825ffSAlexander V. Chernikovclass ObjectsMap(NamedTuple): 332f63825ffSAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] # keyed by ifX 333f63825ffSAlexander V. Chernikov vnet_map: Dict[str, VnetInstance] # keyed by vnetX 334f63825ffSAlexander V. Chernikov topo_map: Dict # self.TOPOLOGY 335f63825ffSAlexander V. Chernikov 336f63825ffSAlexander V. Chernikov 3373873bdc2SAlexander V. Chernikovclass VnetTestTemplate(BaseTest): 3386332ef89SAlexander V. Chernikov NEED_ROOT: bool = True 339cfc9cf9bSAlexander V. Chernikov TOPOLOGY = {} 340cfc9cf9bSAlexander V. Chernikov 341ae8d5881SKristof Provost def _require_default_modules(self): 342ae8d5881SKristof Provost libc.kldload("if_epair.ko") 343ae8d5881SKristof Provost self.require_module("if_epair") 344ae8d5881SKristof Provost 345cfc9cf9bSAlexander V. Chernikov def _get_vnet_handler(self, vnet_alias: str): 346cfc9cf9bSAlexander V. Chernikov handler_name = "{}_handler".format(vnet_alias) 347cfc9cf9bSAlexander V. Chernikov return getattr(self, handler_name, None) 348cfc9cf9bSAlexander V. Chernikov 349cfc9cf9bSAlexander V. Chernikov def _setup_vnet(self, vnet: VnetInstance, obj_map: Dict, pipe): 350cfc9cf9bSAlexander V. Chernikov """Base Handler to setup given VNET. 351cfc9cf9bSAlexander V. Chernikov Can be run in a subprocess. If so, passes control to the special 352cfc9cf9bSAlexander V. Chernikov vnetX_handler() after setting up interface addresses 353cfc9cf9bSAlexander V. Chernikov """ 354cfc9cf9bSAlexander V. Chernikov vnet.attach() 355cfc9cf9bSAlexander V. Chernikov print("# setup_vnet({})".format(vnet.name)) 356f63825ffSAlexander V. Chernikov if pipe is not None: 357f63825ffSAlexander V. Chernikov vnet.set_pipe(pipe) 358cfc9cf9bSAlexander V. Chernikov 359f63825ffSAlexander V. Chernikov topo = obj_map.topo_map 360cfc9cf9bSAlexander V. Chernikov ipv6_ifaces = [] 361cfc9cf9bSAlexander V. Chernikov # Disable DAD 362cfc9cf9bSAlexander V. Chernikov if not vnet.need_dad: 363cfc9cf9bSAlexander V. Chernikov vnet.disable_dad() 364cfc9cf9bSAlexander V. Chernikov for iface in vnet.ifaces: 365cfc9cf9bSAlexander V. Chernikov # check index of vnet within an interface 366cfc9cf9bSAlexander V. Chernikov # as we have prefixes for both ends of the interface 367f63825ffSAlexander V. Chernikov iface_map = obj_map.iface_map[iface.alias] 368cfc9cf9bSAlexander V. Chernikov idx = iface_map.vnet_aliases.index(vnet.alias) 369cfc9cf9bSAlexander V. Chernikov prefixes6 = topo[iface.alias].get("prefixes6", []) 370cfc9cf9bSAlexander V. Chernikov prefixes4 = topo[iface.alias].get("prefixes4", []) 371cfc9cf9bSAlexander V. Chernikov if prefixes6 or prefixes4: 372cfc9cf9bSAlexander V. Chernikov ipv6_ifaces.append(iface) 373cfc9cf9bSAlexander V. Chernikov iface.turn_up() 374cfc9cf9bSAlexander V. Chernikov if prefixes6: 375cfc9cf9bSAlexander V. Chernikov iface.enable_ipv6() 376cfc9cf9bSAlexander V. Chernikov for prefix in prefixes6 + prefixes4: 377584ad412SAlexander V. Chernikov if prefix[idx]: 378cfc9cf9bSAlexander V. Chernikov iface.setup_addr(prefix[idx]) 379cfc9cf9bSAlexander V. Chernikov for iface in ipv6_ifaces: 380cfc9cf9bSAlexander V. Chernikov while iface.has_tentative(): 381cfc9cf9bSAlexander V. Chernikov time.sleep(0.1) 382cfc9cf9bSAlexander V. Chernikov 383cfc9cf9bSAlexander V. Chernikov # Run actual handler 384cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 385cfc9cf9bSAlexander V. Chernikov if handler: 386cfc9cf9bSAlexander V. Chernikov # Do unbuffered stdout for children 387cfc9cf9bSAlexander V. Chernikov # so the logs are present if the child hangs 388cfc9cf9bSAlexander V. Chernikov sys.stdout.reconfigure(line_buffering=True) 3896332ef89SAlexander V. Chernikov self.drop_privileges() 390f63825ffSAlexander V. Chernikov handler(vnet) 391cfc9cf9bSAlexander V. Chernikov 392584ad412SAlexander V. Chernikov def _get_topo_ifmap(self, topo: Dict): 393584ad412SAlexander V. Chernikov iface_factory = IfaceFactory() 394584ad412SAlexander V. Chernikov iface_map: Dict[str, SingleInterfaceMap] = {} 395584ad412SAlexander V. Chernikov iface_aliases = set() 396584ad412SAlexander V. Chernikov for obj_name, obj_data in topo.items(): 397584ad412SAlexander V. Chernikov if obj_name.startswith("vnet"): 398584ad412SAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 399584ad412SAlexander V. Chernikov iface_aliases.add(iface_alias) 400584ad412SAlexander V. Chernikov for iface_alias in iface_aliases: 401584ad412SAlexander V. Chernikov print("Creating {}".format(iface_alias)) 402584ad412SAlexander V. Chernikov iface_data = topo[iface_alias] 403584ad412SAlexander V. Chernikov iface_type = iface_data.get("type", "epair") 404584ad412SAlexander V. Chernikov ifaces = iface_factory.create_iface(iface_alias, iface_type) 405584ad412SAlexander V. Chernikov smap = SingleInterfaceMap(ifaces, []) 406584ad412SAlexander V. Chernikov iface_map[iface_alias] = smap 407584ad412SAlexander V. Chernikov return iface_map 408584ad412SAlexander V. Chernikov 409f63825ffSAlexander V. Chernikov def setup_topology(self, topo: Dict, topology_id: str): 410cfc9cf9bSAlexander V. Chernikov """Creates jails & interfaces for the provided topology""" 411cfc9cf9bSAlexander V. Chernikov vnet_map = {} 412f63825ffSAlexander V. Chernikov vnet_factory = VnetFactory(topology_id) 413584ad412SAlexander V. Chernikov iface_map = self._get_topo_ifmap(topo) 414cfc9cf9bSAlexander V. Chernikov for obj_name, obj_data in topo.items(): 415cfc9cf9bSAlexander V. Chernikov if obj_name.startswith("vnet"): 416cfc9cf9bSAlexander V. Chernikov vnet_ifaces = [] 417cfc9cf9bSAlexander V. Chernikov for iface_alias in obj_data["ifaces"]: 418cfc9cf9bSAlexander V. Chernikov # epair creates 2 interfaces, grab first _available_ 419cfc9cf9bSAlexander V. Chernikov # and map it to the VNET being created 420cfc9cf9bSAlexander V. Chernikov idx = len(iface_map[iface_alias].vnet_aliases) 421cfc9cf9bSAlexander V. Chernikov iface_map[iface_alias].vnet_aliases.append(obj_name) 422cfc9cf9bSAlexander V. Chernikov vnet_ifaces.append(iface_map[iface_alias].ifaces[idx]) 423cfc9cf9bSAlexander V. Chernikov vnet = vnet_factory.create_vnet(obj_name, vnet_ifaces) 424cfc9cf9bSAlexander V. Chernikov vnet_map[obj_name] = vnet 425584ad412SAlexander V. Chernikov # Allow reference to VNETs as attributes 426584ad412SAlexander V. Chernikov setattr(self, obj_name, vnet) 427cfc9cf9bSAlexander V. Chernikov # Debug output 428cfc9cf9bSAlexander V. Chernikov print("============= TEST TOPOLOGY =============") 429cfc9cf9bSAlexander V. Chernikov for vnet_alias, vnet in vnet_map.items(): 430cfc9cf9bSAlexander V. Chernikov print("# vnet {} -> {}".format(vnet.alias, vnet.name), end="") 431cfc9cf9bSAlexander V. Chernikov handler = self._get_vnet_handler(vnet.alias) 432cfc9cf9bSAlexander V. Chernikov if handler: 433cfc9cf9bSAlexander V. Chernikov print(" handler: {}".format(handler.__name__), end="") 434cfc9cf9bSAlexander V. Chernikov print() 435cfc9cf9bSAlexander V. Chernikov for iface_alias, iface_data in iface_map.items(): 436cfc9cf9bSAlexander V. Chernikov vnets = iface_data.vnet_aliases 437cfc9cf9bSAlexander V. Chernikov ifaces: List[VnetInterface] = iface_data.ifaces 438cfc9cf9bSAlexander V. Chernikov if len(vnets) == 1 and len(ifaces) == 2: 439cfc9cf9bSAlexander V. Chernikov print( 440cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> main::{}".format( 441cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, ifaces[1].name 442cfc9cf9bSAlexander V. Chernikov ) 443cfc9cf9bSAlexander V. Chernikov ) 444cfc9cf9bSAlexander V. Chernikov elif len(vnets) == 2 and len(ifaces) == 2: 445cfc9cf9bSAlexander V. Chernikov print( 446cfc9cf9bSAlexander V. Chernikov "# iface {}: {}::{} -> {}::{}".format( 447cfc9cf9bSAlexander V. Chernikov iface_alias, vnets[0], ifaces[0].name, vnets[1], ifaces[1].name 448cfc9cf9bSAlexander V. Chernikov ) 449cfc9cf9bSAlexander V. Chernikov ) 450cfc9cf9bSAlexander V. Chernikov else: 451cfc9cf9bSAlexander V. Chernikov print( 452cfc9cf9bSAlexander V. Chernikov "# iface {}: ifaces: {} vnets: {}".format( 453cfc9cf9bSAlexander V. Chernikov iface_alias, vnets, [i.name for i in ifaces] 454cfc9cf9bSAlexander V. Chernikov ) 455cfc9cf9bSAlexander V. Chernikov ) 456cfc9cf9bSAlexander V. Chernikov print() 457f63825ffSAlexander V. Chernikov return ObjectsMap(iface_map, vnet_map, topo) 458cfc9cf9bSAlexander V. Chernikov 459f63825ffSAlexander V. Chernikov def setup_method(self, _method): 460cfc9cf9bSAlexander V. Chernikov """Sets up all the required topology and handlers for the given test""" 461f63825ffSAlexander V. Chernikov super().setup_method(_method) 462ae8d5881SKristof Provost self._require_default_modules() 463ae8d5881SKristof Provost 464f63825ffSAlexander V. Chernikov # TestIP6Output.test_output6_pktinfo[ipandif] 465f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 466cfc9cf9bSAlexander V. Chernikov topology = self.TOPOLOGY 467cfc9cf9bSAlexander V. Chernikov # First, setup kernel objects - interfaces & vnets 468f63825ffSAlexander V. Chernikov obj_map = self.setup_topology(topology, topology_id) 469cfc9cf9bSAlexander V. Chernikov main_vnet = None # one without subprocess handler 470f63825ffSAlexander V. Chernikov for vnet_alias, vnet in obj_map.vnet_map.items(): 471cfc9cf9bSAlexander V. Chernikov if self._get_vnet_handler(vnet_alias): 472cfc9cf9bSAlexander V. Chernikov # Need subprocess to run 473cfc9cf9bSAlexander V. Chernikov parent_pipe, child_pipe = Pipe() 474cfc9cf9bSAlexander V. Chernikov p = Process( 475cfc9cf9bSAlexander V. Chernikov target=self._setup_vnet, 476cfc9cf9bSAlexander V. Chernikov args=( 477cfc9cf9bSAlexander V. Chernikov vnet, 478cfc9cf9bSAlexander V. Chernikov obj_map, 479cfc9cf9bSAlexander V. Chernikov child_pipe, 480cfc9cf9bSAlexander V. Chernikov ), 481cfc9cf9bSAlexander V. Chernikov ) 482cfc9cf9bSAlexander V. Chernikov vnet.set_pipe(parent_pipe) 483cfc9cf9bSAlexander V. Chernikov vnet.set_subprocess(p) 484cfc9cf9bSAlexander V. Chernikov p.start() 485cfc9cf9bSAlexander V. Chernikov else: 486cfc9cf9bSAlexander V. Chernikov if main_vnet is not None: 487cfc9cf9bSAlexander V. Chernikov raise Exception("there can be only 1 VNET w/o handler") 488cfc9cf9bSAlexander V. Chernikov main_vnet = vnet 489cfc9cf9bSAlexander V. Chernikov # Main vnet needs to be the last, so all the other subprocesses 490cfc9cf9bSAlexander V. Chernikov # are started & their pipe handles collected 491cfc9cf9bSAlexander V. Chernikov self.vnet = main_vnet 492cfc9cf9bSAlexander V. Chernikov self._setup_vnet(main_vnet, obj_map, None) 493cfc9cf9bSAlexander V. Chernikov # Save state for the main handler 494f63825ffSAlexander V. Chernikov self.iface_map = obj_map.iface_map 495f63825ffSAlexander V. Chernikov self.vnet_map = obj_map.vnet_map 4966332ef89SAlexander V. Chernikov self.drop_privileges() 497cfc9cf9bSAlexander V. Chernikov 498cfc9cf9bSAlexander V. Chernikov def cleanup(self, test_id: str): 499cfc9cf9bSAlexander V. Chernikov # pytest test id: file::class::test_name 500f63825ffSAlexander V. Chernikov topology_id = get_topology_id(self.test_id) 501cfc9cf9bSAlexander V. Chernikov 502d4a5d495SJose Luis Duran print("============= vnet cleanup =============") 503f63825ffSAlexander V. Chernikov print("# topology_id: '{}'".format(topology_id)) 504f63825ffSAlexander V. Chernikov VnetFactory(topology_id).cleanup() 505f63825ffSAlexander V. Chernikov IfaceFactory().cleanup() 506cfc9cf9bSAlexander V. Chernikov 507cfc9cf9bSAlexander V. Chernikov def wait_object(self, pipe, timeout=5): 508cfc9cf9bSAlexander V. Chernikov if pipe.poll(timeout): 509cfc9cf9bSAlexander V. Chernikov return pipe.recv() 510cfc9cf9bSAlexander V. Chernikov raise TimeoutError 511cfc9cf9bSAlexander V. Chernikov 512584ad412SAlexander V. Chernikov def wait_objects_any(self, pipe_list, timeout=5): 513584ad412SAlexander V. Chernikov objects = connection.wait(pipe_list, timeout) 514584ad412SAlexander V. Chernikov if objects: 515584ad412SAlexander V. Chernikov return objects[0].recv() 516584ad412SAlexander V. Chernikov raise TimeoutError 517584ad412SAlexander V. Chernikov 518f63825ffSAlexander V. Chernikov def send_object(self, pipe, obj): 519f63825ffSAlexander V. Chernikov pipe.send(obj) 520f63825ffSAlexander V. Chernikov 521584ad412SAlexander V. Chernikov def wait(self): 522584ad412SAlexander V. Chernikov while True: 523584ad412SAlexander V. Chernikov time.sleep(1) 524584ad412SAlexander V. Chernikov 525cfc9cf9bSAlexander V. Chernikov @property 526cfc9cf9bSAlexander V. Chernikov def curvnet(self): 527cfc9cf9bSAlexander V. Chernikov pass 528cfc9cf9bSAlexander V. Chernikov 529cfc9cf9bSAlexander V. Chernikov 530cfc9cf9bSAlexander V. Chernikovclass SingleVnetTestTemplate(VnetTestTemplate): 5318eb2bee6SAlexander V. Chernikov IPV6_PREFIXES: List[str] = [] 5328eb2bee6SAlexander V. Chernikov IPV4_PREFIXES: List[str] = [] 533f3065e76SAlexander V. Chernikov IFTYPE = "epair" 5348eb2bee6SAlexander V. Chernikov 535f3065e76SAlexander V. Chernikov def _setup_default_topology(self): 536cfc9cf9bSAlexander V. Chernikov topology = copy.deepcopy( 537cfc9cf9bSAlexander V. Chernikov { 538cfc9cf9bSAlexander V. Chernikov "vnet1": {"ifaces": ["if1"]}, 539f3065e76SAlexander V. Chernikov "if1": {"type": self.IFTYPE, "prefixes4": [], "prefixes6": []}, 540cfc9cf9bSAlexander V. Chernikov } 541cfc9cf9bSAlexander V. Chernikov ) 542cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV6_PREFIXES: 543cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes6"].append((prefix,)) 544cfc9cf9bSAlexander V. Chernikov for prefix in self.IPV4_PREFIXES: 545cfc9cf9bSAlexander V. Chernikov topology["if1"]["prefixes4"].append((prefix,)) 546f3065e76SAlexander V. Chernikov return topology 547f3065e76SAlexander V. Chernikov 548f3065e76SAlexander V. Chernikov def setup_method(self, method): 549f3065e76SAlexander V. Chernikov if not getattr(self, "TOPOLOGY", None): 550f3065e76SAlexander V. Chernikov self.TOPOLOGY = self._setup_default_topology() 551f3065e76SAlexander V. Chernikov else: 552f3065e76SAlexander V. Chernikov names = self.TOPOLOGY.keys() 553f3065e76SAlexander V. Chernikov assert len([n for n in names if n.startswith("vnet")]) == 1 554cfc9cf9bSAlexander V. Chernikov super().setup_method(method) 555