1
0
mirror of https://github.com/simh/simh.git synced 2026-01-11 23:52:58 +00:00

ETHER: Add vmnet support for macOS Catalina 10.15.7 through Big Sur 11.7.10

This commit is contained in:
Mark Pizzolato 2025-08-14 08:55:49 -10:00
parent df31a5b7f9
commit caea0c0970
6 changed files with 297 additions and 215 deletions

View File

@ -108,6 +108,7 @@ Simulator binaries for x86 Linus, x86 macOS, and Windows for all recent changes
- Ethernet enhancements on all platforms and and much simplified platform support on macOS.
- Almost all existing simulators have useful SET CONSOLE SPEED=nnn behaviors in both the bare console session as well as TELNET console sessions.
- Simh global/startup configuration commands can be contained in the first of simh.ini found in: 1) The current working directory, 2) The User login directory, 3) The directory containing the simulator executable, 4) The ~/Library/Preferences directory, 5) The /Library/Preferences directory, or 6) The /etc directory.
- Enhanced Ethernet functionality on macOS which leverages the OS provided vmnet capabilities. This is available on all intel and Apple Silicon macOS systems starting with Catalina (10.15) which was released in 2019.
#### All simulators build cleanly under OpenVMS on ia64 systems.
@ -138,7 +139,7 @@ Simulator binaries for x86 Linus, x86 macOS, and Windows for all recent changes
- VAX Instruction history can be recorded to disk both for all instructions executed as well as every n instructions.
- VAX Unibus simulators (780, 750, 730, 8600, 8200) run DEC supplied diagnostics at the speed of the original systems and also run the privileged instruction diagnostic that was supported on the original systems.
### All relevant changes in the simh v3.12-4 release have been merged into this repo
### All relevant changes in Bob Supnik's simh v3.12-4 release have been merged into this repo
### Bill Beech has made significant enhancements and bug fixes to the SWTP simulators along with a new disk controller from Roberto Sancho Villa

View File

@ -972,12 +972,18 @@ ifeq (${WIN32},) #*nix Environments (&& cygwin)
else
DONT_USE_VMNET = $(shell if ${TEST} $(macOSMajor) -lt 10; then echo DONT_USE_VMNET; fi)
endif
ifeq (,$(DONT_USE_VMNET)$(DONT_USE_VMNET_HOST))
DONT_USE_VMNET_HOST = $(shell if ${TEST} $(macOSMajor) -lt 11; then echo DONT_USE_VMNET_HOST; fi)
endif
endif
endif
ifneq (,$(if $(DONT_USE_VMNET),,$(call find_include,vmnet.framework/Headers/vmnet)))
# sim_ether reduces network features to the appropriate minimal set
NETWORK_LAN_FEATURES += VMNET
NETWORK_CCDEFS += -DUSE_SHARED -I slirp -I slirp_glue -I slirp_glue/qemu -DHAVE_VMNET_NETWORK
ifneq (,$(DONT_USE_VMNET_HOST))
NETWORK_CCDEFS += -DDONT_USE_VMNET_HOST
endif
NETWORK_DEPS += slirp/*.c slirp_glue/*.c
NETWORK_LDFLAGS += -framework vmnet
$(info using vmnet: $(call find_include,vmnet.framework/Headers/vmnet))

23
scp.c
View File

@ -7438,6 +7438,9 @@ if (1) {
fprintf (st, "\n curl tool: %s", curlversion);
setenv ("SIM_CURL_CMD_AVAILABLE", "TRUE", 1);
}
#if !defined(_WIN32)
fprintf (st, "\n %s as root", _sim_running_as_root () ? "Running" : "Not running");
#endif
}
#endif
if ((!strcmp (os_type, "Unknown")) && (getenv ("OSTYPE")))
@ -16904,6 +16907,26 @@ if (sim_rand_seed < 0)
return (sim_rand_seed - 1);
}
t_bool _sim_running_as_root (void)
{
FILE *f = fopen("/dev/mem", "r");
char response[64] = "";
if (f != NULL) {
fclose(f);
return TRUE;
}
#if !defined(_WIN32)
f = popen("id -u", "r");
if (f == NULL)
return FALSE;
if (fgets(response, sizeof(response), f))
sim_trim_endspc (response);
pclose(f);
#endif
return (0 == strcmp(response, "0"));
}
typedef struct MFILE {
char *buf;

View File

@ -816,26 +816,6 @@ fields.pseudo_hash = eth_crc32 (eth_crc32 (0x6ba7b815, sysid, sizeof(sysid)), ta
memcpy (uuid_buf, (void *)&fields, 16);
}
static ETH_BOOL _eth_running_as_root (void)
{
FILE *f = fopen("/dev/mem", "r");
char response[64] = "";
if (f != NULL) {
fclose(f);
return TRUE;
}
#if !defined(_WIN32)
f = popen("id -u", "r");
if (f == NULL)
return FALSE;
if (fgets(response, sizeof(response), f))
sim_trim_endspc (response);
pclose(f);
#endif
return (0 == strcmp(response, "0"));
}
#if defined (USE_NETWORK) || defined (USE_SHARED)
#ifdef HAVE_VMNET_NETWORK
@ -1036,8 +1016,6 @@ t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
}
} while (end != NULL);
}
if ((NULL != strstr(list[0].name, "tap:")) && (!_eth_running_as_root()))
fprintf(st, "You probably need to run as root\n");
}
}
if (1) {
@ -1069,6 +1047,17 @@ t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
eth_show_dev (st, eth_open_devices[i]);
}
}
if (((NULL != strstr(list[0].name, "tap:")) ||
(NULL != strstr(list[0].name, "nat:")) ||
(NULL != strstr(list[0].name, "udp:"))) ||
#if defined(_WIN32)
FALSE)
fprintf(st, "You probably need to install npcap\n");
#else
((list[0].eth_api == ETH_API_VMNET) &&
(!_sim_running_as_root())))
fprintf(st, "You probably need to run as root\n");
#endif
return SCPE_OK;
}
@ -1295,29 +1284,31 @@ if (1) {
continue;
if (sim_isspace(line[0])) {
c = strchr(line, ':');
if (strstr(line, "IPv4 Address") || strstr(line, "IP Address")) {
char *c = strchr(line, ':');
strlcat(dev->info, "Host IPv4 Address", sizeof(dev->info));
strlcat(dev->info, c, sizeof(dev->info));
}
if (strstr(line, "Subnet Mask")) {
unsigned int byt1, byt2, byt3, byt4;
int netmask;
char cidr[20];
sscanf(c, ": %u.%u.%u.%u", &byt1, &byt2, &byt3, &byt4);
netmask = (byt1 << 24) | (byt2 << 16) | (byt3 << 8) | byt4;
for (i = 0; i < 32; i++) {
if (0 == (netmask & (1 << (31 - i))))
break;
if (dev != NULL) {
if (strstr(line, "IPv4 Address") || strstr(line, "IP Address")) {
char *c = strchr(line, ':');
strlcat(dev->info, "Host IPv4 Address", sizeof(dev->info));
strlcat(dev->info, c, sizeof(dev->info));
}
if (strstr(line, "Subnet Mask")) {
unsigned int byt1, byt2, byt3, byt4;
int netmask;
char cidr[20];
sscanf(c, ": %u.%u.%u.%u", &byt1, &byt2, &byt3, &byt4);
netmask = (byt1 << 24) | (byt2 << 16) | (byt3 << 8) | byt4;
for (i = 0; i < 32; i++) {
if (0 == (netmask & (1 << (31 - i))))
break;
}
snprintf(cidr, sizeof(cidr), "/%d", i);
strlcat(dev->info, cidr, sizeof(dev->info));
}
if (strstr(line, "Media State")) {
if (dev->info[0] != '\0')
strlcat(dev->info, "\n", sizeof(dev->info));
strlcat(dev->info, "MediaState: disconnected", sizeof(dev->info));
}
snprintf(cidr, sizeof(cidr), "/%d", i);
strlcat(dev->info, cidr, sizeof(dev->info));
}
if (strstr(line, "Media State")) {
if (dev->info[0] != '\0')
strlcat(dev->info, "\n", sizeof(dev->info));
strlcat(dev->info, "MediaState: disconnected", sizeof(dev->info));
}
continue;
}
@ -1412,7 +1403,10 @@ const char *eth_capabilities(void)
#endif
strlcat (capabilities, "Ethernet Packet transports", sizeof (capabilities));
#if defined (HAVE_VMNET_NETWORK)
strlcat (capabilities, ":PCAP(vmnet):TAP(vmnet)", sizeof (capabilities));
strlcat (capabilities, ":PCAP(vmnet)", sizeof (capabilities));
#if defined(USE_VMNET_HOST_AS_TAP)
strlcat (capabilities, ":TAP(vmnet)", sizeof (capabilities));
#endif
#if defined(USE_VMNET_SHARED_AS_NAT)
strlcat (capabilities, ":NAT(vmnet)", sizeof (capabilities));
#endif
@ -1420,7 +1414,7 @@ const char *eth_capabilities(void)
#if defined (HAVE_PCAP_NETWORK)
strlcat (capabilities, ":PCAP", sizeof (capabilities));
#endif
#if defined (HAVE_TAP_NETWORK)
#if defined (HAVE_TAP_NETWORK) && !defined (USE_VMNET_HOST_AS_TAP)
strlcat (capabilities, ":TAP", sizeof (capabilities));
#endif
#if defined (HAVE_VDE_NETWORK)
@ -1512,12 +1506,14 @@ if (1) {
}
xpc_release(interface_list);
#if defined(USE_VMNET_HOST_AS_TAP)
if (used < max) {
sprintf(list[used].name, "%s", "tap:{optional-tap-parameters}");
sprintf(list[used].desc, "%s", "Integrated host-only network (vmnet) support");
list[used].eth_api = ETH_API_VMNET;
++used;
}
#endif
#if defined(USE_VMNET_SHARED_AS_NAT)
if (used < max) {
sprintf(list[used].name, "%s", "nat:{optional-nat-parameters}");
@ -2150,7 +2146,7 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on, c
"nmcli device 2>/dev/null | grep %.*s 2>/dev/null | grep -e 'wifi' -e 'ethernet' 2>/dev/null | awk '{ print $2 }'",
NULL};
const char *LinkStatuspatterns[] = {
"ipconfig getsummary %.*s 2>/dev/null | grep 'LinkStatusActive' 2>/dev/null | awk '{ print $3 }'",
"ifconfig %.*s 2>/dev/null | grep 'status: ' 2>/dev/null | awk '{if ($0 ~ /status: inactive/) { print \"MediaState: disconnected\" } else {if ($0 ~ /status: active/) { print \"MediaState: connected\" }}}'",
NULL};
const char *LinkStatusDownpatterns[] = {
"ip -4 link show %.*s 2>/dev/null | grep 'NO-CARRIER' 2>/dev/null | grep 'state DOWN' 2>/dev/null",
@ -2283,6 +2279,49 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on, c
}
}
}
if ((Info != NULL) && (NULL == strstr(Info, "LinkType"))) {
char command[64] = "networksetup -listallhardwareports";
char tool[CBUFSIZE];
FILE *f;
ETH_LIST *dev = NULL;
get_glyph_nc (command, tool, 0);
if (sim_get_tool_path (tool)[0]) {
if (NULL != (f = popen(command, "r"))) {
char port_type[128];
char line[128];
memset(line, 0, sizeof(line));
memset(port_type, 0, sizeof(port_type));
while (fgets(line, sizeof(line), f)) {
sim_trim_endspc (line);
if (line[0] == '\0')
continue;
if (memcmp(line, "Hardware Port: ", 15) == 0) {
strlcpy(port_type, line + 15, sizeof(port_type));
continue;
}
if ((strcmp(port_type, "Ethernet") != 0) && (strcmp(port_type, "Wi-Fi") != 0))
continue;
if (memcmp(line, "Device: ", 8) == 0) {
if (strcmp(devname, line + 8) == 0) {
if (Info[0] != '\0')
strlcat(Info, "\n", ETH_DEV_INFO_MAX);
strlcat(Info, "LinkType: ", ETH_DEV_INFO_MAX);
if (strcmp(port_type, "Wi-Fi") == 0)
strlcat(Info, "WiFi", ETH_DEV_INFO_MAX);
else
strlcat(Info, "Ethernet", ETH_DEV_INFO_MAX);
break;
}
else
continue;
}
}
pclose(f);
}
}
}
/* If the link type is still undetermined, then assume that device */
/* names starting with "wl" are WiFi otherwise they are Ethernet */
if ((Info != NULL) && (NULL == strstr(Info, "LinkType"))) {
@ -2294,7 +2333,7 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on, c
else
strlcat(Info, "Ethernet", ETH_DEV_INFO_MAX);
}
for (i=0; LinkStatuspatterns[i] && (Info != NULL) && (NULL == strstr(Info, "LinkStatus")); ++i) {
for (i=0; LinkStatuspatterns[i] && (Info != NULL) && (NULL == strstr(Info, "MediaState")); ++i) {
snprintf(command, sizeof(command), LinkStatuspatterns[i], (int)(sizeof(command) - (2 + strlen(LinkStatuspatterns[i]))), devname);
get_glyph_nc (command, tool, 0);
if (sim_get_tool_path (tool)[0]) {
@ -2302,27 +2341,19 @@ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname, int set_on, c
while (NULL == strstr(Info, "MediaState")) {
if (fgets(result, sizeof(result), f)) {
sim_trim_endspc(result);
if (Info[0] != '\0')
strlcat(Info, "\n", ETH_DEV_INFO_MAX);
strlcat(Info, "MediaState: ", ETH_DEV_INFO_MAX);
if (0 == strcasecmp(result, "TRUE"))
strlcat(Info, "connected", ETH_DEV_INFO_MAX);
else
strlcat(Info, "disconnected", ETH_DEV_INFO_MAX);
break;
}
else {
if (Info[0] != '\0')
strlcat(Info, "\n", ETH_DEV_INFO_MAX);
strlcat(Info, "MediaState: disconnected", ETH_DEV_INFO_MAX);
break;
if (result[0]) {
if (Info[0] != '\0')
strlcat(Info, "\n", ETH_DEV_INFO_MAX);
strlcat(Info, result, ETH_DEV_INFO_MAX);
break;
}
}
}
pclose(f);
}
}
}
for (i=0; LinkStatusDownpatterns[i] && (Info != NULL) && (NULL == strstr(Info, "LinkStatus")); ++i) {
for (i=0; LinkStatusDownpatterns[i] && (Info != NULL) && (NULL == strstr(Info, "MediaState")); ++i) {
snprintf(command, sizeof(command), LinkStatusDownpatterns[i], (int)(sizeof(command) - (2 + strlen(LinkStatusDownpatterns[i]))), devname);
get_glyph_nc (command, tool, 0);
if (sim_get_tool_path (tool)[0]) {
@ -2853,149 +2884,7 @@ if (0 == strncmp("nat:", savname, 4)) {
#endif /* defined(HAVE_SLIRP_NETWORK) */
}
#endif /* !defined(USE_VMNET_SHARED_AS_NAT) */
/* Setting parameters on a vmnet SHARED interface doesn't actually */
/* work as described in the documentation. If, in the future, it */
/* does, and the network block can actually be specified along with */
/* configuring inbound UDP and TCP port mapping, this can be enabled */
/* along with implementing any currently missing pieces. */
#if defined(HAVE_VMNET_NETWORK)
xpc_object_t if_desc;
dispatch_queue_t vmn_queue;
interface_ref vmn_interface;
char gbuf[CBUFSIZE];
char *hostaddr = NULL;
char *endaddr = NULL;
char *netmask = NULL;
if_desc = xpc_dictionary_create(NULL, NULL, 0);
#if defined(USE_VMNET_SHARED_AS_NAT)
if (0 == strncasecmp(savname, "nat:", 4)) {
NAT nat;
int err = 0;
memset(&nat, 0, sizeof(NAT));
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_SHARED_MODE);
/* First, start with the original NAT default */
sim_nat_parse_args (&nat, "GATEWAY=10.0.2.2,MASKLEN=24,DHCP=10.0.2.15", ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
err = sim_nat_parse_args (&nat, &savname[4], ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
if (err == 0) {
if (nat.vdhcp_start.s_addr != INADDR_ANY)
hostaddr = strdup (inet_ntoa (nat.vdhcp_start));
if (nat.vdhcp_end.s_addr != INADDR_ANY)
endaddr = strdup (inet_ntoa (nat.vdhcp_end));
if (nat.vnetmask.s_addr != INADDR_ANY)
netmask = strdup (inet_ntoa (nat.vnetmask));
if (hostaddr)
xpc_dictionary_set_string(if_desc, vmnet_start_address_key, hostaddr);
if (endaddr)
xpc_dictionary_set_string(if_desc, vmnet_end_address_key, endaddr);
if (netmask)
xpc_dictionary_set_string(if_desc, vmnet_host_subnet_mask_key, netmask);
xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, TRUE);
}
// Need NAT parameter parsing, TCP and UDP port allowances and setup
}
else {
#else /* !defined(USE_VMNET_SHARED_AS_NAT) */
if (1) {
#endif /* !defined(USE_VMNET_SHARED_AS_NAT) */
if (0 == strncasecmp(savname, "tap:", 4)) {
const char *tptr = savname + 4;
unsigned char net_uuid[16];
NAT nat;
int err = 0;
memset(&nat, 0, sizeof(nat));
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_HOST_MODE);
tptr = get_glyph_nc (tptr, gbuf, ',');
_eth_tap_uuid (gbuf, net_uuid);
xpc_dictionary_set_uuid(if_desc, vmnet_network_identifier_key, net_uuid);
if ((tptr != NULL) && (*tptr != '\0')) {
err = sim_nat_parse_args (&nat, tptr, ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
if (err != 0) {
xpc_release(if_desc);
snprintf (errbuf, errbuf_size, "Error parsing vmnet tap: device parameters");
return SCPE_OPENERR;
}
if (nat.vgateway.s_addr != INADDR_ANY)
hostaddr = strdup (inet_ntoa (nat.vgateway));
if (nat.vnetmask.s_addr != INADDR_NONE)
netmask = strdup (inet_ntoa (nat.vnetmask));
if (((hostaddr != NULL) && (netmask == NULL)) ||
((hostaddr == NULL) && (netmask != NULL))){
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
snprintf (errbuf, errbuf_size, "Both the HOSTIP and MASKLEN must be specified together vmnet host (tap:) device");
return SCPE_OPENERR;
}
if (hostaddr)
xpc_dictionary_set_string(if_desc, vmnet_host_ip_address_key, hostaddr);
if (netmask)
xpc_dictionary_set_string(if_desc, vmnet_host_subnet_mask_key, netmask);
}
}
else { // Bridged, so check if we've got a reasonable device name
xpc_object_t interface_list = vmnet_copy_shared_interface_list();
int i, interface_count = xpc_array_get_count(interface_list);
for (i=0; i<interface_count; i++) {
xpc_object_t element = xpc_array_get_value(interface_list, i);
if (0 == strcmp(savname, xpc_string_get_string_ptr(element)))
break;
}
if (i == interface_count) { /* Not found? */
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
snprintf (errbuf, errbuf_size, "You must specify either an appropriate vmnet bridged interface or a tap:");
return SCPE_OPENERR;
}
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_BRIDGED_MODE);
xpc_dictionary_set_string(if_desc, vmnet_shared_interface_name_key, savname);
}
}
vmn_queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
_open_port_vmnet_cb_finished = dispatch_semaphore_create(0);
_open_port_opaque = opaque;
vmn_interface = vmnet_start_interface(if_desc, vmn_queue, ^(vmnet_return_t status, xpc_object_t params)
{
_open_port_vmnet_status = status;
if (status == VMNET_SUCCESS) {
/* The logical device (bridge or otherwise) vmnet provides has its own MAC address which */
/* becomes the host system's address from the simulator's point of view */
if (SCPE_OK == eth_mac_scan (&((ETH_DEV*)_open_port_opaque)->host_nic_phy_hw_addr, xpc_dictionary_get_string(params, vmnet_mac_address_key)))
((ETH_DEV*)_open_port_opaque)->have_host_nic_phy_addr = 1;
}
dispatch_semaphore_signal(_open_port_vmnet_cb_finished);
});
dispatch_semaphore_wait(_open_port_vmnet_cb_finished, DISPATCH_TIME_FOREVER);
dispatch_release(_open_port_vmnet_cb_finished);
_open_port_vmnet_cb_finished = NULL;
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
if (_open_port_vmnet_status != VMNET_SUCCESS) {
if (!_eth_running_as_root())
snprintf (errbuf, errbuf_size, "Failed to create vmnet connection for %s - %s. You may need to run as root", savname, _vmnet_status_string(_open_port_vmnet_status));
else
snprintf (errbuf, errbuf_size, "Failed to create vmnet connection for %s - %s.", savname, _vmnet_status_string(_open_port_vmnet_status));
return SCPE_OPENERR;
}
*eth_api = ETH_API_VMNET;
*handle = (void *)vmn_interface; /* Flag used to indicated open */
return SCPE_OK;
#else /* !defined(HAVE_VMNET_NETWORK) */
#if !defined(USE_VMNET_HOST_AS_TAP)
if (0 == strncmp("tap:", savname, 4)) {
int tun = -1; /* TUN/TAP Socket */
int on = 1;
@ -3076,7 +2965,7 @@ if (0 == strncmp("tap:", savname, 4)) {
close(s);
}
}
#endif
#endif /* defined (__APPLE__) */
}
else
strlcpy(errbuf, strerror(errno), errbuf_size);
@ -3085,16 +2974,166 @@ if (0 == strncmp("tap:", savname, 4)) {
tun = -1;
}
}
#else
#else /* !(defined(HAVE_BSDTUNTAP) && defined(HAVE_TAP_NETWORK)) */
strlcpy(errbuf, "No support for tap: devices", errbuf_size);
#endif /* !defined(__linux) && !defined(HAVE_BSDTUNTAP) */
if (0 == errbuf[0]) {
*eth_api = ETH_API_TAP;
*handle = (void *)1; /* Flag used to indicated open */
return SCPE_OK;
#endif /* !(defined(HAVE_BSDTUNTAP) && defined(HAVE_TAP_NETWORK)) */
}
#endif /* !defined(USE_VMNET_HOST_AS_TAP) */
/* Setting parameters on a vmnet SHARED interface doesn't actually */
/* work as described in the documentation. If, in the future, it */
/* does, and the network block can actually be specified along with */
/* configuring inbound UDP and TCP port mapping, this can be enabled */
/* along with implementing any then currently missing pieces. */
#if defined(HAVE_VMNET_NETWORK)
xpc_object_t if_desc;
dispatch_queue_t vmn_queue;
interface_ref vmn_interface;
char gbuf[CBUFSIZE];
char *hostaddr = NULL;
char *endaddr = NULL;
char *netmask = NULL;
if_desc = xpc_dictionary_create(NULL, NULL, 0);
#if defined(USE_VMNET_SHARED_AS_NAT)
if (0 == strncmp(savname, "nat:", 4)) {
NAT nat;
int err = 0;
memset(&nat, 0, sizeof(NAT));
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_SHARED_MODE);
/* First, start with the original NAT default */
sim_nat_parse_args (&nat, "GATEWAY=10.0.2.2,MASKLEN=24,DHCP=10.0.2.15", ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
err = sim_nat_parse_args (&nat, &savname[4], ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
if (err == 0) {
if (nat.vdhcp_start.s_addr != INADDR_ANY)
hostaddr = strdup (inet_ntoa (nat.vdhcp_start));
if (nat.vdhcp_end.s_addr != INADDR_ANY)
endaddr = strdup (inet_ntoa (nat.vdhcp_end));
if (nat.vnetmask.s_addr != INADDR_ANY)
netmask = strdup (inet_ntoa (nat.vnetmask));
if (hostaddr)
xpc_dictionary_set_string(if_desc, vmnet_start_address_key, hostaddr);
if (endaddr)
xpc_dictionary_set_string(if_desc, vmnet_end_address_key, endaddr);
if (netmask)
xpc_dictionary_set_string(if_desc, vmnet_host_subnet_mask_key, netmask);
xpc_dictionary_set_bool(if_desc, vmnet_enable_isolation_key, TRUE);
}
// Need NAT parameter parsing, TCP and UDP port allowances and setup
}
#endif /* defined(USE_VMNET_SHARED_AS_NAT) */
#if defined(USE_VMNET_HOST_AS_TAP)
if (0 == strncmp(savname, "tap:", 4)) {
const char *tptr = savname + 4;
unsigned char net_uuid[16];
NAT nat;
int err = 0;
memset(&nat, 0, sizeof(nat));
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_HOST_MODE);
tptr = get_glyph_nc (tptr, gbuf, ',');
_eth_tap_uuid (gbuf, net_uuid);
xpc_dictionary_set_uuid(if_desc, vmnet_network_identifier_key, net_uuid);
if ((tptr != NULL) && (*tptr != '\0')) {
err = sim_nat_parse_args (&nat, tptr, ETH_API_VMNET, (char *)errbuf, sizeof(errbuf));
if (err != 0) {
xpc_release(if_desc);
snprintf (errbuf, errbuf_size, "Error parsing vmnet tap: device parameters");
return SCPE_OPENERR;
}
if (nat.vgateway.s_addr != INADDR_ANY)
hostaddr = strdup (inet_ntoa (nat.vgateway));
if (nat.vnetmask.s_addr != INADDR_NONE)
netmask = strdup (inet_ntoa (nat.vnetmask));
if (((hostaddr != NULL) && (netmask == NULL)) ||
((hostaddr == NULL) && (netmask != NULL))){
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
snprintf (errbuf, errbuf_size, "Both the HOSTIP and MASKLEN must be specified together vmnet host (tap:) device");
return SCPE_OPENERR;
}
if (hostaddr)
xpc_dictionary_set_string(if_desc, vmnet_host_ip_address_key, hostaddr);
if (netmask)
xpc_dictionary_set_string(if_desc, vmnet_host_subnet_mask_key, netmask);
}
}
#else /* !defined(USE_VMNET_HOST_AS_TAP) */
if (0 == strncmp(savname, "tap:", 4)) {
free(hostaddr);
free(endaddr);
free(netmask);
strlcpy(errbuf, "No support for tap: devices", errbuf_size);
return SCPE_OPENERR;
}
#endif /* !defined(USE_VMNET_HOST_AS_TAP) */
if ((0 != strncmp(savname, "nat:", 4)) &&
(0 != strncmp(savname, "tap:", 4))) {
/* Try bridged, so check if we've got a reasonable device name */
xpc_object_t interface_list = vmnet_copy_shared_interface_list();
int i, interface_count = xpc_array_get_count(interface_list);
for (i=0; i<interface_count; i++) {
xpc_object_t element = xpc_array_get_value(interface_list, i);
if (0 == strcmp(savname, xpc_string_get_string_ptr(element)))
break;
}
if (i == interface_count) { /* Matching interface not found? */
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
#if defined(USE_VMNET_HOST_AS_TAP)
snprintf (errbuf, errbuf_size, "%s is not a valid network specification. You must specify either an appropriate vmnet bridged interface or a tap:", savname);
#else /* !defined(USE_VMNET_HOST_AS_TAP) */
snprintf (errbuf, errbuf_size, "%s is not a valid network specification. You must specify an appropriate vmnet bridged interface", savname);
#endif /* !defined(USE_VMNET_HOST_AS_TAP) */
return SCPE_OPENERR;
}
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_BRIDGED_MODE);
xpc_dictionary_set_string(if_desc, vmnet_shared_interface_name_key, savname);
}
vmn_queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
_open_port_vmnet_cb_finished = dispatch_semaphore_create(0);
_open_port_opaque = opaque;
vmn_interface = vmnet_start_interface(if_desc, vmn_queue, ^(vmnet_return_t status, xpc_object_t params)
{
_open_port_vmnet_status = status;
if (status == VMNET_SUCCESS) {
/* The logical device (bridge or otherwise) vmnet provides has its own MAC address which */
/* becomes the host system's address from the simulator's point of view */
if (SCPE_OK == eth_mac_scan (&((ETH_DEV*)_open_port_opaque)->host_nic_phy_hw_addr, xpc_dictionary_get_string(params, vmnet_mac_address_key)))
((ETH_DEV*)_open_port_opaque)->have_host_nic_phy_addr = 1;
}
dispatch_semaphore_signal(_open_port_vmnet_cb_finished);
});
dispatch_semaphore_wait(_open_port_vmnet_cb_finished, DISPATCH_TIME_FOREVER);
dispatch_release(_open_port_vmnet_cb_finished);
_open_port_vmnet_cb_finished = NULL;
free(hostaddr);
free(endaddr);
free(netmask);
xpc_release(if_desc);
if (_open_port_vmnet_status != VMNET_SUCCESS) {
if (!_sim_running_as_root())
snprintf (errbuf, errbuf_size, "Failed to create vmnet connection for %s - %s. You may need to run as root", savname, _vmnet_status_string(_open_port_vmnet_status));
else
snprintf (errbuf, errbuf_size, "Failed to create vmnet connection for %s - %s.", savname, _vmnet_status_string(_open_port_vmnet_status));
return SCPE_OPENERR;
}
*eth_api = ETH_API_VMNET;
*handle = (void *)vmn_interface; /* Flag used to indicated open */
return SCPE_OK;
#else /* !defined(HAVE_VMNET_NETWORK) */
if (0 == strncmp("vde:", savname, 4)) {
#if defined(HAVE_VDE_NETWORK)
if (eth_vde_network_available) {
@ -3453,7 +3492,16 @@ static char version[300] = "";
if (version[0] != '\0')
return version;
#if defined(HAVE_VMNET_NETWORK)
strlcat (version, "PCAP(vmnet - bridged), TAP(vmnet - host)", sizeof (version));
strlcat (version, "PCAP(vmnet - bridged)", sizeof (version));
#if defined(USE_VMNET_HOST_AS_TAP)
strlcat (version, ", TAP(vmnet - host)", sizeof (version));
#else /* !defined(USE_VMNET_HOST_AS_TAP) */
#if defined(HAVE_TAP_NETWORK)
if (version[0] != '\0')
strlcat (version, ", ", sizeof (version));
strlcat (version, "TAP", sizeof (version));
#endif
#endif /* !defined(USE_VMNET_HOST_AS_TAP) */
#if defined(USE_VMNET_SHARED_AS_NAT)
strlcat (version, ", NAT(vmnet - shared), UDP", sizeof (version));
#else /* !defined(USE_VMNET_SHARED_AS_NAT) */

View File

@ -150,9 +150,12 @@ extern "C" {
/* Generally avoid pcap APIs when running with vmnet.framework */
#if defined(HAVE_VMNET_NETWORK)
#define DONT_USE_PCAP_FINDALLDEVS 1
#if !defined(DONT_USE_VMNET_HOST)
#define USE_VMNET_HOST_AS_TAP 1
#if defined(HAVE_TAP_NETWORK)
#undef HAVE_TAP_NETWORK
#endif
#endif
#if defined(HAVE_VDE_NETWORK)
#undef HAVE_VDE_NETWORK
#endif

View File

@ -407,6 +407,7 @@ struct SEND {
/* Private SCP only APIs */
t_stat _sim_os_putchar (int32 out);
t_bool _sim_running_as_root (void);
#endif /* defined(SIM_SCP_PRIVATE_DONT_REPEAT) */