From f5e1956dfac95211156f0e143da049d1b4405793 Mon Sep 17 00:00:00 2001 From: grossmj Date: Sat, 16 Jul 2022 11:38:51 +0200 Subject: [PATCH 1/3] Support user defined loader/libraries to run IOU --- gns3server/compute/iou/iou_vm.py | 47 +++++++++++++++++++++++++++----- tests/compute/iou/test_iou_vm.py | 6 ++-- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 63e9f528..4e2315e8 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -84,6 +84,8 @@ class IOUVM(BaseNode): self._started = False self._nvram_watcher = None self._path = self.manager.get_abs_image_path(path, project.path) + self._lib_base = self.manager.get_images_directory() + self._loader = None self._license_check = True # IOU settings @@ -143,6 +145,7 @@ class IOUVM(BaseNode): """ self._path = self.manager.get_abs_image_path(path, self.project.path) + self._loader = None log.info(f'IOU "{self._name}" [{self._id}]: IOU image updated to "{self._path}"') @property @@ -174,9 +177,10 @@ class IOUVM(BaseNode): Finds the default RAM and NVRAM values for the IOU image. """ + await self._check_requirements() try: output = await gns3server.utils.asyncio.subprocess_check_output( - self._path, "-h", cwd=self.working_dir, stderr=True + *self._loader, self._path, "-h", cwd=self.working_dir, stderr=True ) match = re.search(r"-n \s+Size of nvram in Kb \(default ([0-9]+)KB\)", output) if match: @@ -191,11 +195,13 @@ class IOUVM(BaseNode): await self.update_default_iou_values() - def _check_requirements(self): + async def _check_requirements(self): """ Checks the IOU image. """ + if self._loader is not None: + return # image already checked if not self._path: raise IOUError("IOU image is not configured") if not os.path.isfile(self._path) or not os.path.exists(self._path): @@ -219,6 +225,28 @@ class IOUVM(BaseNode): if not os.access(self._path, os.X_OK): raise IOUError(f"IOU image '{self._path}' is not executable") + # set loader command + if elf_header_start[4] == b"\x01": + # 32-bit loader + loader = os.path.join(self._lib_base, "lib", "ld-linux.so.2") + lib_path = (os.path.join(self._lib_base, "lib"), + os.path.join(self._lib_base, "lib", "i386-linux-gnu")) + else: + # 64-bit loader + loader = os.path.join(self._lib_base, "lib64", "ld-linux-x86-64.so.2") + lib_path = (os.path.join(self._lib_base, "lib64"), + os.path.join(self._lib_base, "lib", "x86_64-linux-gnu")) + self._loader = [] + if os.path.isfile(loader): + try: + proc = await asyncio.create_subprocess_exec(loader, "--verify", self._path) + if await proc.wait() == 0: + self._loader = [loader, "--library-path", ":".join(lib_path)] + else: + log.warning(f"Loader {loader} incompatible with '{self._path}'") + except (OSError, subprocess.SubprocessError) as e: + log.warning(f"Could not use loader {loader}: {e}") + def asdict(self): iou_vm_info = { @@ -385,8 +413,10 @@ class IOUVM(BaseNode): Checks for missing shared library dependencies in the IOU image. """ + env = os.environ.copy() + env["LD_TRACE_LOADED_OBJECTS"] = "1" try: - output = await gns3server.utils.asyncio.subprocess_check_output("ldd", self._path) + output = await gns3server.utils.asyncio.subprocess_check_output(*self._loader, self._path, env=env) except (OSError, subprocess.SubprocessError) as e: log.warning(f"Could not determine the shared library dependencies for {self._path}: {e}") return @@ -513,7 +543,7 @@ class IOUVM(BaseNode): Starts the IOU process. """ - self._check_requirements() + await self._check_requirements() if not self.is_running(): await self._library_check() @@ -560,10 +590,13 @@ class IOUVM(BaseNode): command = await self._build_command() try: - log.info(f"Starting IOU: {command}") + if self._loader: + log.info(f"Starting IOU: {command} with loader {self._loader}") + else: + log.info(f"Starting IOU: {command}") self.command_line = " ".join(command) self._iou_process = await asyncio.create_subprocess_exec( - *command, + *self._loader, *command, stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE, stderr=subprocess.STDOUT, @@ -1125,7 +1158,7 @@ class IOUVM(BaseNode): env["IOURC"] = self.iourc_path try: output = await gns3server.utils.asyncio.subprocess_check_output( - self._path, "-h", cwd=self.working_dir, env=env, stderr=True + *self._loader, self._path, "-h", cwd=self.working_dir, env=env, stderr=True ) if re.search(r"-l\s+Enable Layer 1 keepalive messages", output): command.extend(["-l"]) diff --git a/tests/compute/iou/test_iou_vm.py b/tests/compute/iou/test_iou_vm.py index 8a1cf845..afd6fb78 100644 --- a/tests/compute/iou/test_iou_vm.py +++ b/tests/compute/iou/test_iou_vm.py @@ -48,6 +48,7 @@ async def vm(compute_project, manager, config, tmpdir, fake_iou_bin, iourc_file) vm = IOUVM("test", str(uuid.uuid4()), compute_project, manager, application_id=1) config.settings.IOU.iourc_path = iourc_file vm.path = "iou.bin" + vm._loader = [] return vm @@ -228,7 +229,8 @@ def test_path_relative(vm, fake_iou_bin): assert vm.path == fake_iou_bin -def test_path_invalid_bin(vm, tmpdir, config): +@pytest.mark.asyncio +async def test_path_invalid_bin(vm, tmpdir, config): config.settings.Server.images_path = str(tmpdir) path = str(tmpdir / "test.bin") @@ -238,7 +240,7 @@ def test_path_invalid_bin(vm, tmpdir, config): with pytest.raises(IOUError): vm.path = path - vm._check_requirements() + await vm._check_requirements() def test_create_netmap_config(vm): From d022b211dcf10f0da1b728ae2f067857923b275f Mon Sep 17 00:00:00 2001 From: grossmj Date: Sun, 17 Jul 2022 23:55:34 +0200 Subject: [PATCH 2/3] Fix check for 32-bit in ELF header --- gns3server/compute/iou/iou_vm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gns3server/compute/iou/iou_vm.py b/gns3server/compute/iou/iou_vm.py index 4e2315e8..9926dcc6 100644 --- a/gns3server/compute/iou/iou_vm.py +++ b/gns3server/compute/iou/iou_vm.py @@ -226,7 +226,7 @@ class IOUVM(BaseNode): raise IOUError(f"IOU image '{self._path}' is not executable") # set loader command - if elf_header_start[4] == b"\x01": + if elf_header_start[4] == 1: # 32-bit loader loader = os.path.join(self._lib_base, "lib", "ld-linux.so.2") lib_path = (os.path.join(self._lib_base, "lib"), From 7f5ad8e22526165b0a2518e5eeb00f7e0b35aa21 Mon Sep 17 00:00:00 2001 From: grossmj Date: Wed, 20 Jul 2022 16:39:24 +0200 Subject: [PATCH 3/3] Ignore image detection for IOU user libraries in image directory --- gns3server/db/tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gns3server/db/tasks.py b/gns3server/db/tasks.py index f6ec4125..4a106ffe 100644 --- a/gns3server/db/tasks.py +++ b/gns3server/db/tasks.py @@ -91,7 +91,8 @@ async def get_computes(app: FastAPI) -> List[dict]: def image_filter(change: Change, path: str) -> bool: if change == Change.added: - if path.endswith(".tmp") or path.endswith(".md5sum") or path.startswith("."): + if path.endswith(".tmp") or path.endswith(".md5sum") or path.startswith(".") or \ + "/lib/" in path or "/lib64/" in path: return False header_magic_len = 7 with open(path, "rb") as f: