diff --git a/gns3server/compute/builtin/nodes/cloud.py b/gns3server/compute/builtin/nodes/cloud.py index 8a336bbd..94ca0823 100644 --- a/gns3server/compute/builtin/nodes/cloud.py +++ b/gns3server/compute/builtin/nodes/cloud.py @@ -362,12 +362,12 @@ class Cloud(BaseNode): """ # Wireless adapters are not well supported by the libpcap on OSX - if (await self._is_wifi_adapter_osx(port_info["interface"])): + if await self._is_wifi_adapter_osx(port_info["interface"]): raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS") if port_info["interface"].startswith("vmnet"): # Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them) await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name, - interface=port_info["interface"])) + interface=port_info["interface"])) return if not gns3server.utils.interfaces.has_netmask(port_info["interface"]): raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"])) diff --git a/gns3server/compute/vmware/__init__.py b/gns3server/compute/vmware/__init__.py index 59357490..934de130 100644 --- a/gns3server/compute/vmware/__init__.py +++ b/gns3server/compute/vmware/__init__.py @@ -27,6 +27,7 @@ import asyncio import subprocess import logging import codecs +import ipaddress from collections import OrderedDict from gns3server.utils.interfaces import interfaces @@ -51,6 +52,7 @@ class VMware(BaseManager): self._vmrun_path = None self._host_type = None self._vmnets = [] + self._vmnets_info = {} self._vmnet_start_range = 2 if sys.platform.startswith("win"): self._vmnet_end_range = 19 @@ -273,7 +275,7 @@ class VMware(BaseManager): else: # location on Linux vmware_networking_file = "/etc/vmware/networking" - vmnet_interfaces = [] + vmnet_interfaces = {} try: with open(vmware_networking_file, "r", encoding="utf-8") as f: for line in f.read().splitlines(): @@ -281,7 +283,20 @@ class VMware(BaseManager): if match: vmnet = "vmnet{}".format(match.group(1)) if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): - vmnet_interfaces.append(vmnet) + vmnet_interfaces[vmnet] = {} + with open(vmware_networking_file, "r", encoding="utf-8") as f: + for line in f.read().splitlines(): + match = re.search(r"VNET_([0-9]+)_HOSTONLY_SUBNET\s+(.*)", line) + if match: + vmnet = "vmnet{}".format(match.group(1)) + if vmnet in vmnet_interfaces.keys(): + vmnet_interfaces[vmnet]["subnet"] = match.group(2) + match = re.search(r"VNET_([0-9]+)_HOSTONLY_NETMASK\s+(.*)", line) + if match: + vmnet = "vmnet{}".format(match.group(1)) + if vmnet in vmnet_interfaces.keys(): + vmnet_interfaces[vmnet]["netmask"] = match.group(2) + except OSError as e: raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) return vmnet_interfaces @@ -324,6 +339,25 @@ class VMware(BaseManager): raise VMwareError("No VMnet interface available between vmnet{} and vmnet{}. Go to preferences VMware / Network / Configure to add more interfaces.".format(self._vmnet_start_range, self._vmnet_end_range)) return self._vmnets.pop(0) + def find_bridge_interface(self, vmnet_interface): + """ + Find the bridge interface that is used for the vmnet interface in VMware. + """ + + if vmnet_interface in self._vmnets_info.keys(): + subnet = self._vmnets_info[vmnet_interface].get("subnet", None) + netmask = self._vmnets_info[vmnet_interface].get("netmask", None) + if subnet and netmask: + for interface in interfaces(): + try: + network = ipaddress.ip_network(f"{subnet}/{netmask}") + ip = ipaddress.ip_address(interface["ip_address"]) + except ValueError: + continue + if ip in network: + return interface["name"] + return None + def refresh_vmnet_list(self, ubridge=True): if ubridge: @@ -332,6 +366,8 @@ class VMware(BaseManager): else: vmnet_interfaces = self._get_vmnet_interfaces() + self._vmnets_info = vmnet_interfaces.copy() + vmnet_interfaces = list(vmnet_interfaces.keys()) # remove vmnets already in use for vmware_vm in self._nodes.values(): for used_vmnet in vmware_vm.vmnets: @@ -734,5 +770,4 @@ class VMware(BaseManager): if __name__ == '__main__': loop = asyncio.get_event_loop() vmware = VMware.instance() - print("=> Check version") loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version())) diff --git a/gns3server/compute/vmware/vmware_vm.py b/gns3server/compute/vmware/vmware_vm.py index 28b67ca2..959fea06 100644 --- a/gns3server/compute/vmware/vmware_vm.py +++ b/gns3server/compute/vmware/vmware_vm.py @@ -23,9 +23,11 @@ import sys import os import asyncio import tempfile +import platform from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer from gns3server.utils.asyncio.serial import asyncio_open_serial +from gns3server.utils import parse_version from gns3server.utils.asyncio import locking from collections import OrderedDict from .vmware_error import VMwareError @@ -252,8 +254,13 @@ class VMwareVM(BaseNode): if self._get_vmx_setting(connected): del self._vmx_pairs[connected] + use_ubridge = True + # use alternative method to find vmnet interfaces on macOS >= 11.0 (BigSur) + # because "bridge" interfaces are used instead and they are only created on the VM starts + if sys.platform.startswith("darwin") and parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"): + use_ubridge = False + self.manager.refresh_vmnet_list(ubridge=use_ubridge) # then configure VMware network adapters - self.manager.refresh_vmnet_list() for adapter_number in range(0, self._adapters): custom_adapter = self._get_custom_adapter_settings(adapter_number) @@ -333,8 +340,17 @@ class VMwareVM(BaseNode): vmnet_interface = os.path.basename(self._vmx_pairs[vnet]) if sys.platform.startswith("darwin"): - # special case on OSX, we cannot bind VMnet interfaces using the libpcap - await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface)) + if parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"): + # a bridge interface (bridge100, bridge101 etc.) is used instead of a vmnet interface + # on macOS >= 11.0 (Big Sur) + vmnet_interface = self.manager.find_bridge_interface(vmnet_interface) + if not vmnet_interface: + raise VMwareError(f"fCould not find bridge interface linked with {vmnet_interface}") + block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False) + await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic) + else: + # special case on macOS, we cannot bind VMnet interfaces using the libpcap + await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface)) else: block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False) await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic) @@ -426,7 +442,7 @@ class VMwareVM(BaseNode): if self.status == "started": return - if (await self.is_running()): + if await self.is_running(): raise VMwareError("The VM is already running in VMware") ubridge_path = self.ubridge_path @@ -476,7 +492,7 @@ class VMwareVM(BaseNode): await self._stop_ubridge() try: - if (await self.is_running()): + if await self.is_running(): if self.on_close == "save_vm_state": await self._control_vm("suspend") elif self.on_close == "shutdown_signal": @@ -728,7 +744,7 @@ class VMwareVM(BaseNode): # check for the connection type connection_type = "ethernet{}.connectiontype".format(adapter_number) if not self._use_any_adapter and connection_type in self._vmx_pairs and self._vmx_pairs[connection_type] in ("nat", "bridged", "hostonly"): - if (await self.is_running()): + if await self.is_running(): raise VMwareError("Attachment '{attachment}' is configured on network adapter {adapter_number}. " "Please stop VMware VM '{name}' to link to this adapter and allow GNS3 to change the attachment type.".format(attachment=self._vmx_pairs[connection_type], adapter_number=adapter_number,