Fix VMware support on macOS BigSur

pull/1913/head
grossmj 3 years ago
parent bfd30f3547
commit c892cf371b

@ -362,12 +362,12 @@ class Cloud(BaseNode):
""" """
# Wireless adapters are not well supported by the libpcap on OSX # 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") raise NodeError("Connecting to a Wireless adapter is not supported on Mac OS")
if port_info["interface"].startswith("vmnet"): if port_info["interface"].startswith("vmnet"):
# Use a special NIO to connect to VMware vmnet interfaces on OSX (libpcap doesn't support them) # 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, await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=bridge_name,
interface=port_info["interface"])) interface=port_info["interface"]))
return return
if not gns3server.utils.interfaces.has_netmask(port_info["interface"]): if not gns3server.utils.interfaces.has_netmask(port_info["interface"]):
raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"])) raise NodeError("Interface {} has no netmask, interface down?".format(port_info["interface"]))

@ -27,6 +27,7 @@ import asyncio
import subprocess import subprocess
import logging import logging
import codecs import codecs
import ipaddress
from collections import OrderedDict from collections import OrderedDict
from gns3server.utils.interfaces import interfaces from gns3server.utils.interfaces import interfaces
@ -51,6 +52,7 @@ class VMware(BaseManager):
self._vmrun_path = None self._vmrun_path = None
self._host_type = None self._host_type = None
self._vmnets = [] self._vmnets = []
self._vmnets_info = {}
self._vmnet_start_range = 2 self._vmnet_start_range = 2
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
self._vmnet_end_range = 19 self._vmnet_end_range = 19
@ -273,7 +275,7 @@ class VMware(BaseManager):
else: else:
# location on Linux # location on Linux
vmware_networking_file = "/etc/vmware/networking" vmware_networking_file = "/etc/vmware/networking"
vmnet_interfaces = [] vmnet_interfaces = {}
try: try:
with open(vmware_networking_file, "r", encoding="utf-8") as f: with open(vmware_networking_file, "r", encoding="utf-8") as f:
for line in f.read().splitlines(): for line in f.read().splitlines():
@ -281,7 +283,20 @@ class VMware(BaseManager):
if match: if match:
vmnet = "vmnet{}".format(match.group(1)) vmnet = "vmnet{}".format(match.group(1))
if vmnet not in ("vmnet0", "vmnet1", "vmnet8"): 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: except OSError as e:
raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e)) raise VMwareError("Cannot open {}: {}".format(vmware_networking_file, e))
return vmnet_interfaces 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)) 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) 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): def refresh_vmnet_list(self, ubridge=True):
if ubridge: if ubridge:
@ -332,6 +366,8 @@ class VMware(BaseManager):
else: else:
vmnet_interfaces = self._get_vmnet_interfaces() vmnet_interfaces = self._get_vmnet_interfaces()
self._vmnets_info = vmnet_interfaces.copy()
vmnet_interfaces = list(vmnet_interfaces.keys())
# remove vmnets already in use # remove vmnets already in use
for vmware_vm in self._nodes.values(): for vmware_vm in self._nodes.values():
for used_vmnet in vmware_vm.vmnets: for used_vmnet in vmware_vm.vmnets:
@ -734,5 +770,4 @@ class VMware(BaseManager):
if __name__ == '__main__': if __name__ == '__main__':
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
vmware = VMware.instance() vmware = VMware.instance()
print("=> Check version")
loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version())) loop.run_until_complete(asyncio.ensure_future(vmware.check_vmware_version()))

@ -23,9 +23,11 @@ import sys
import os import os
import asyncio import asyncio
import tempfile import tempfile
import platform
from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer from gns3server.utils.asyncio.telnet_server import AsyncioTelnetServer
from gns3server.utils.asyncio.serial import asyncio_open_serial from gns3server.utils.asyncio.serial import asyncio_open_serial
from gns3server.utils import parse_version
from gns3server.utils.asyncio import locking from gns3server.utils.asyncio import locking
from collections import OrderedDict from collections import OrderedDict
from .vmware_error import VMwareError from .vmware_error import VMwareError
@ -252,8 +254,13 @@ class VMwareVM(BaseNode):
if self._get_vmx_setting(connected): if self._get_vmx_setting(connected):
del self._vmx_pairs[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 # then configure VMware network adapters
self.manager.refresh_vmnet_list()
for adapter_number in range(0, self._adapters): for adapter_number in range(0, self._adapters):
custom_adapter = self._get_custom_adapter_settings(adapter_number) 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]) vmnet_interface = os.path.basename(self._vmx_pairs[vnet])
if sys.platform.startswith("darwin"): if sys.platform.startswith("darwin"):
# special case on OSX, we cannot bind VMnet interfaces using the libpcap if parse_version(platform.mac_ver()[0]) >= parse_version("11.0.0"):
await self._ubridge_send('bridge add_nio_fusion_vmnet {name} "{interface}"'.format(name=vnet, interface=vmnet_interface)) # 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: else:
block_host_traffic = self.manager.config.get_section_config("VMware").getboolean("block_host_traffic", False) 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) await self._add_ubridge_ethernet_connection(vnet, vmnet_interface, block_host_traffic)
@ -426,7 +442,7 @@ class VMwareVM(BaseNode):
if self.status == "started": if self.status == "started":
return return
if (await self.is_running()): if await self.is_running():
raise VMwareError("The VM is already running in VMware") raise VMwareError("The VM is already running in VMware")
ubridge_path = self.ubridge_path ubridge_path = self.ubridge_path
@ -476,7 +492,7 @@ class VMwareVM(BaseNode):
await self._stop_ubridge() await self._stop_ubridge()
try: try:
if (await self.is_running()): if await self.is_running():
if self.on_close == "save_vm_state": if self.on_close == "save_vm_state":
await self._control_vm("suspend") await self._control_vm("suspend")
elif self.on_close == "shutdown_signal": elif self.on_close == "shutdown_signal":
@ -728,7 +744,7 @@ class VMwareVM(BaseNode):
# check for the connection type # check for the connection type
connection_type = "ethernet{}.connectiontype".format(adapter_number) 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 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}. " 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], "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, adapter_number=adapter_number,

Loading…
Cancel
Save