mirror of
https://github.com/open-simh/simh.git
synced 2026-01-11 23:53:30 +00:00
Initial support for vmnet.framework
On macOS, tap devices for L2 networking are not supported out of the box. While a kext can be added to provide tap support, the kext experience is not very good; Apple has strongly recommended against their usage. As a replacement that's documented and recommended, Apple introduced the vmnet framework, intended for emulators and virtualization software explicitly. This API requires macOS 10.10, with bridged network support coming in macOS 10.15. This introduces basic support for vmnet.framework in SIMH. I've tested it by booting an emulated MicroVAX 3800 from an emulated InfoServer 150, where it was able to reach OpenVMS 7.3 standalone BACKUP.
This commit is contained in:
parent
ad3e744951
commit
361ef76dbc
@ -199,6 +199,9 @@ option(WITH_VDE
|
||||
option(WITH_TAP
|
||||
"Enable (=1)/disable (=0) TAP/TUN device network support. (def: enabled)"
|
||||
TRUE)
|
||||
option(WITH_VMNET
|
||||
"Enable (=1)/disable (=0) macOS vmnet.framework network support. (def: enabled)"
|
||||
TRUE)
|
||||
option(WITH_VIDEO
|
||||
"Enable (=1)/disable (=0) simulator display and graphics support (def: enabled)"
|
||||
TRUE)
|
||||
|
||||
@ -351,6 +351,11 @@ if (WITH_NETWORK)
|
||||
list(APPEND NETWORK_PKG_STATUS "NAT(SLiRP)")
|
||||
endif (WITH_SLIRP)
|
||||
|
||||
if (WITH_VMNET AND APPLE)
|
||||
target_link_libraries(simh_network INTERFACE "-framework vmnet")
|
||||
target_compile_definitions(simh_network INTERFACE HAVE_VMNET_NETWORK)
|
||||
endif(WITH_VMNET AND APPLE)
|
||||
|
||||
## Finally, set the network runtime
|
||||
if (NOT network_runtime)
|
||||
## Default to USE_SHARED... USE_NETWORK is deprecated.
|
||||
|
||||
158
sim_ether.c
158
sim_ether.c
@ -1005,6 +1005,9 @@ const char *eth_capabilities(void)
|
||||
#endif
|
||||
#if defined (HAVE_SLIRP_NETWORK)
|
||||
":NAT"
|
||||
#endif
|
||||
#if defined (HAVE_VMNET_NETWORK)
|
||||
":VMNET"
|
||||
#endif
|
||||
":UDP";
|
||||
}
|
||||
@ -1196,6 +1199,14 @@ if (used < max) {
|
||||
++used;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
if (used < max) {
|
||||
sprintf(list[used].name, "%s", "vmnet:{optional-parameters}");
|
||||
sprintf(list[used].desc, "%s", "Integrated vmnet.framework support");
|
||||
list[used].eth_api = ETH_API_VMN;
|
||||
++used;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (used < max) {
|
||||
sprintf(list[used].name, "%s", "udp:sourceport:remotehost:remoteport");
|
||||
@ -1245,6 +1256,10 @@ extern "C" {
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
#include <vmnet/vmnet.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_SHARED) && (defined(_WIN32) || defined(SIM_HAVE_DLOPEN))
|
||||
/* Dynamic DLL loading technique and modified source comes from
|
||||
Etherial/WireShark capture_pcap.c */
|
||||
@ -2060,6 +2075,37 @@ while (dev->handle) {
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_VDE_NETWORK */
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
case ETH_API_VMN:
|
||||
{
|
||||
vmnet_return_t ret;
|
||||
int count = 1;
|
||||
struct pcap_pkthdr header;
|
||||
struct vmpktdesc pkt_desc;
|
||||
struct iovec iov;
|
||||
|
||||
// XXX: Should be MTU returned from vmnet startup?
|
||||
u_char buf[ETH_MAX_JUMBO_FRAME];
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = ETH_MAX_JUMBO_FRAME;
|
||||
|
||||
pkt_desc.vm_pkt_size = ETH_MAX_JUMBO_FRAME;
|
||||
pkt_desc.vm_pkt_iov = &iov;
|
||||
pkt_desc.vm_pkt_iovcnt = 1;
|
||||
pkt_desc.vm_flags = 0;
|
||||
|
||||
ret = vmnet_read((interface_ref)dev->handle, &pkt_desc, &count);
|
||||
if (ret == VMNET_SUCCESS && count > 0) {
|
||||
status = 1;
|
||||
header.caplen = header.len = pkt_desc.vm_pkt_size;
|
||||
_eth_callback((u_char *)dev, &header, buf);
|
||||
} else {
|
||||
status = ret == VMNET_SUCCESS ? 0 : -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_SLIRP_NETWORK
|
||||
case ETH_API_NAT:
|
||||
sim_slirp_dispatch ((SLIRP*)dev->handle);
|
||||
@ -2244,6 +2290,47 @@ if (bufsz < ETH_MAX_JUMBO_FRAME)
|
||||
|
||||
/* attempt to connect device */
|
||||
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
|
||||
|
||||
#if defined(HAVE_VMNET_NETWORK)
|
||||
if (0 == strncmp("vmnet:", savname, 6)) {
|
||||
xpc_object_t if_desc;
|
||||
dispatch_queue_t vmn_queue;
|
||||
interface_ref vmn_interface;
|
||||
// __block is used so it can be captured in the callback
|
||||
__block vmnet_return_t vmn_status;
|
||||
// Because vmnet operates via callbacks, set up a semaphore to block on
|
||||
dispatch_semaphore_t cb_finished;
|
||||
|
||||
if_desc = xpc_dictionary_create(NULL, NULL, 0);
|
||||
// VMNET_HOST_MODE: Host and other guests
|
||||
// VMNET_SHARED_MODE: NAT
|
||||
// VMNET_BRIDGED_MODE: Requires macOS 10.15
|
||||
// XXX: Support other modes.
|
||||
xpc_dictionary_set_uint64(if_desc, vmnet_operation_mode_key, VMNET_HOST_MODE);
|
||||
|
||||
vmn_queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
|
||||
|
||||
cb_finished = dispatch_semaphore_create(0);
|
||||
|
||||
vmn_interface = vmnet_start_interface(if_desc, vmn_queue, ^(vmnet_return_t status, xpc_object_t params){
|
||||
vmn_status = status;
|
||||
|
||||
dispatch_semaphore_signal(cb_finished);
|
||||
});
|
||||
dispatch_semaphore_wait(cb_finished, DISPATCH_TIME_FOREVER);
|
||||
dispatch_release(cb_finished);
|
||||
xpc_release(if_desc);
|
||||
|
||||
if (vmn_status != VMNET_SUCCESS) {
|
||||
return sim_messagef (SCPE_OPENERR, "Eth: Failed to create vmnet (vmnet_return_t: %d)\n", vmn_status);
|
||||
}
|
||||
|
||||
*eth_api = ETH_API_VMN;
|
||||
*handle = (void *)vmn_interface; /* Flag used to indicated open */
|
||||
return SCPE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (0 == strncmp("tap:", savname, 4)) {
|
||||
int tun = -1; /* TUN/TAP Socket */
|
||||
int on = 1;
|
||||
@ -2646,6 +2733,15 @@ switch (eth_api) {
|
||||
case ETH_API_NAT:
|
||||
sim_slirp_close((SLIRP*)pcap);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
case ETH_API_VMN:
|
||||
{
|
||||
dispatch_queue_t stop_queue;
|
||||
stop_queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
|
||||
vmnet_stop_interface((interface_ref)pcap, stop_queue, ^(vmnet_return_t status){});
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ETH_API_UDP:
|
||||
sim_close_sock(pcap_fd);
|
||||
@ -2960,6 +3056,9 @@ switch (dev->eth_api) {
|
||||
case ETH_API_NAT:
|
||||
netname = "nat";
|
||||
break;
|
||||
case ETH_API_VMN:
|
||||
netname = "vmnet";
|
||||
break;
|
||||
}
|
||||
sprintf(msg, "%s(%s): ", where, netname);
|
||||
switch (dev->eth_api) {
|
||||
@ -2967,6 +3066,12 @@ switch (dev->eth_api) {
|
||||
case ETH_API_PCAP:
|
||||
sim_printf ("%s%s\n", msg, pcap_geterr ((pcap_t*)dev->handle));
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_VMNET_NETWORK)
|
||||
case ETH_API_VMN:
|
||||
/* XXX: vmnet errors aren't global */
|
||||
sim_printf ("%s\n", msg);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
sim_err_sock (INVALID_SOCKET, msg);
|
||||
@ -3088,6 +3193,27 @@ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) {
|
||||
else
|
||||
status = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
case ETH_API_VMN:
|
||||
{
|
||||
vmnet_return_t ret;
|
||||
int count = 1;
|
||||
struct vmpktdesc pkt_desc;
|
||||
struct iovec iov;
|
||||
|
||||
iov.iov_base = packet->msg;
|
||||
iov.iov_len = packet->len;
|
||||
|
||||
pkt_desc.vm_pkt_size = packet->len;
|
||||
pkt_desc.vm_pkt_iov = &iov;
|
||||
pkt_desc.vm_pkt_iovcnt = 1;
|
||||
pkt_desc.vm_flags = 0;
|
||||
|
||||
ret = vmnet_write((interface_ref)dev->handle, &pkt_desc, &count);
|
||||
status = (ret == VMNET_SUCCESS && count > 0) ? 0 : 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ETH_API_UDP:
|
||||
status = (((int32)packet->len == sim_write_sock (dev->fd_handle, (char *)packet->msg, (int32)packet->len)) ? 0 : -1);
|
||||
@ -3712,6 +3838,7 @@ switch (dev->eth_api) {
|
||||
case ETH_API_VDE:
|
||||
case ETH_API_UDP:
|
||||
case ETH_API_NAT:
|
||||
case ETH_API_VMN:
|
||||
bpf_used = 0;
|
||||
to_me = 0;
|
||||
eth_packet_trace (dev, data, header->len, "received");
|
||||
@ -3908,6 +4035,37 @@ do {
|
||||
}
|
||||
break;
|
||||
#endif /* HAVE_VDE_NETWORK */
|
||||
#ifdef HAVE_VMNET_NETWORK
|
||||
case ETH_API_VMN:
|
||||
{
|
||||
vmnet_return_t ret;
|
||||
int count = 1;
|
||||
struct pcap_pkthdr header;
|
||||
struct vmpktdesc pkt_desc;
|
||||
struct iovec iov;
|
||||
|
||||
// XXX: Should be MTU returned from vmnet startup?
|
||||
u_char buf[ETH_MAX_JUMBO_FRAME];
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = ETH_MAX_JUMBO_FRAME;
|
||||
|
||||
pkt_desc.vm_pkt_size = ETH_MAX_JUMBO_FRAME;
|
||||
pkt_desc.vm_pkt_iov = &iov;
|
||||
pkt_desc.vm_pkt_iovcnt = 1;
|
||||
pkt_desc.vm_flags = 0;
|
||||
|
||||
ret = vmnet_read((interface_ref)dev->handle, &pkt_desc, &count);
|
||||
if (ret == VMNET_SUCCESS && count > 0) {
|
||||
status = 1;
|
||||
header.caplen = header.len = pkt_desc.vm_pkt_size;
|
||||
_eth_callback((u_char *)dev, &header, buf);
|
||||
} else {
|
||||
status = ret == VMNET_SUCCESS ? 0 : -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case ETH_API_UDP:
|
||||
if (1) {
|
||||
struct pcap_pkthdr header;
|
||||
|
||||
@ -265,6 +265,7 @@ struct eth_device {
|
||||
#define ETH_API_VDE 3 /* VDE API in use */
|
||||
#define ETH_API_UDP 4 /* UDP API in use */
|
||||
#define ETH_API_NAT 5 /* NAT (SLiRP) API in use */
|
||||
#define ETH_API_VMN 6 /* Apple vmnet.framework in use */
|
||||
ETH_PCALLBACK read_callback; /* read callback function */
|
||||
ETH_PCALLBACK write_callback; /* write callback function */
|
||||
ETH_PACK* read_packet; /* read packet */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user