2023-05-04 21:12:02 +02:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* VDE networking for 86box
|
|
|
|
|
* See https://wiki.virtualsquare.org/#!tutorials/vdebasics.md
|
|
|
|
|
* for basic information about VDE. You can browse the source
|
|
|
|
|
* code at https://github.com/virtualsquare/vde-2
|
|
|
|
|
*
|
|
|
|
|
* VDE support is only available in Linux and MacOS. It _should_
|
|
|
|
|
* be available in BSD*, and if someday there is a BSD version of
|
|
|
|
|
* 86box this _could_ work out of the box.
|
|
|
|
|
*
|
|
|
|
|
* Authors: jguillaumes <jguillaumes@gmail.com>
|
|
|
|
|
* Copyright 2023 jguillaumes.
|
|
|
|
|
*
|
|
|
|
|
* See the COPYING file at the top of the 86box for license details.
|
|
|
|
|
* TL;DR: GPL version 2.
|
|
|
|
|
*/
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#else
|
|
|
|
|
#error VDE is not supported under windows
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <libvdeplug.h>
|
|
|
|
|
|
|
|
|
|
#define HAVE_STDARG_H
|
|
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/device.h>
|
|
|
|
|
#include <86box/plat.h>
|
|
|
|
|
#include <86box/plat_dynld.h>
|
|
|
|
|
#include <86box/thread.h>
|
|
|
|
|
#include <86box/timer.h>
|
|
|
|
|
#include <86box/network.h>
|
|
|
|
|
#include <86box/net_event.h>
|
|
|
|
|
|
|
|
|
|
#define VDE_PKT_BATCH NET_QUEUE_LEN
|
|
|
|
|
#define VDE_DESCRIPTION "86Box virtual card"
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
NET_EVENT_STOP = 0,
|
|
|
|
|
NET_EVENT_TX,
|
|
|
|
|
NET_EVENT_RX,
|
|
|
|
|
NET_EVENT_VDE,
|
|
|
|
|
NET_EVENT_MAX
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static volatile void *libvde_handle = NULL;
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// VDE connection structure
|
|
|
|
|
//-
|
|
|
|
|
typedef struct {
|
|
|
|
|
void *vdeconn; // VDEPLUG Connection
|
|
|
|
|
netcard_t *card; // NIC linked to
|
|
|
|
|
thread_t *poll_tid; // Polling thread
|
|
|
|
|
net_evt_t tx_event; // Packets to transmit event
|
|
|
|
|
net_evt_t stop_event; // Stop thread event
|
|
|
|
|
netpkt_t pkt; // Packet read/sent
|
|
|
|
|
netpkt_t pktv[VDE_PKT_BATCH]; // Packet queue
|
|
|
|
|
uint8_t mac_addr[6]; // MAC Address
|
|
|
|
|
} net_vde_t;
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// VDE libvdeplug function pointers
|
|
|
|
|
//-
|
|
|
|
|
static VDECONN *(*f_vde_open)(char *, char *, int, struct vde_open_args *); // This is vde_open_real()
|
|
|
|
|
static void (*f_vde_close)(VDECONN *);
|
|
|
|
|
static int (*f_vde_datafd)(VDECONN *); // Get the data (read/write) handle
|
|
|
|
|
static int (*f_vde_ctlfd)(VDECONN *); // Get the control handle
|
|
|
|
|
static ssize_t (*f_vde_recv)(VDECONN *, void *, size_t, int); // Receive a packet
|
|
|
|
|
static ssize_t (*f_vde_send)(VDECONN *, const void *, size_t, int); // Send a packet
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// VDE libvdeplug function table (for import)
|
|
|
|
|
//-
|
|
|
|
|
static dllimp_t vde_imports[] = {
|
|
|
|
|
{"vde_open_real", &f_vde_open},
|
|
|
|
|
{"vde_close", &f_vde_close},
|
|
|
|
|
{"vde_datafd", &f_vde_datafd},
|
|
|
|
|
{"vde_ctlfd", &f_vde_ctlfd},
|
|
|
|
|
{"vde_recv", &f_vde_recv},
|
|
|
|
|
{"vde_send", &f_vde_send},
|
|
|
|
|
{ NULL, NULL}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_VDE_LOG
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
int vde_do_log = ENABLE_VDE_LOG;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
vde_log(const char *fmt, ...) {
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
if (vde_do_log) {
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
|
pclog_ex(fmt, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
# define vde_log(fmt, ...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
#error VDE networking is not supported under windows
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// VDE thread
|
|
|
|
|
//-
|
|
|
|
|
static void net_vde_thread(void *priv) {
|
|
|
|
|
net_vde_t *vde = (net_vde_t *) priv;
|
|
|
|
|
vde_log("VDE: Polling started.\n");
|
|
|
|
|
|
|
|
|
|
struct pollfd pfd[NET_EVENT_MAX];
|
|
|
|
|
pfd[NET_EVENT_STOP].fd = net_event_get_fd(&vde->stop_event);
|
|
|
|
|
pfd[NET_EVENT_STOP].events = POLLIN | POLLPRI;
|
|
|
|
|
|
|
|
|
|
pfd[NET_EVENT_TX].fd = net_event_get_fd(&vde->tx_event);
|
|
|
|
|
pfd[NET_EVENT_TX].events = POLLIN | POLLPRI;
|
|
|
|
|
|
|
|
|
|
pfd[NET_EVENT_RX].fd = f_vde_datafd(vde->vdeconn);
|
|
|
|
|
pfd[NET_EVENT_RX].events = POLLIN;
|
|
|
|
|
|
|
|
|
|
pfd[NET_EVENT_VDE].fd = f_vde_ctlfd(vde->vdeconn);
|
|
|
|
|
pfd[NET_EVENT_VDE].events = POLLIN;
|
|
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
|
poll(pfd, NET_EVENT_MAX, -1);
|
|
|
|
|
|
|
|
|
|
// Acvity in the control handle means the link is closed
|
|
|
|
|
// We send ourselves a STOP event
|
|
|
|
|
if (pfd[NET_EVENT_VDE].revents & POLLIN) {
|
|
|
|
|
net_event_set(&vde->stop_event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There are packets queued to transmit
|
|
|
|
|
if (pfd[NET_EVENT_TX].revents & POLLIN) {
|
|
|
|
|
net_event_clear(&vde->tx_event);
|
|
|
|
|
int packets = network_tx_popv(vde->card, vde->pktv, VDE_PKT_BATCH);
|
|
|
|
|
for (int i=0; i<packets; i++) {
|
|
|
|
|
int nc = f_vde_send(vde->vdeconn, vde->pktv[i].data,vde->pktv[i].len, 0 );
|
|
|
|
|
if (nc == 0) {
|
|
|
|
|
vde_log("VDE: Problem, no bytes sent.\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Packets are available for reading. Read packet and queue it
|
|
|
|
|
if (pfd[NET_EVENT_RX].revents & POLLIN) {
|
|
|
|
|
int nc = f_vde_recv(vde->vdeconn, vde->pkt.data, NET_MAX_FRAME, 0);
|
|
|
|
|
vde->pkt.len = nc;
|
|
|
|
|
network_rx_put_pkt(vde->card, &vde->pkt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We have been told to close
|
|
|
|
|
if (pfd[NET_EVENT_STOP].revents & POLLIN) {
|
|
|
|
|
net_event_clear(&vde->stop_event);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
vde_log("VDE: Polling stopped.\n");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// Prepare the VDE libvdeplug interface.
|
|
|
|
|
// Load the dynamic library libvdeplug.
|
|
|
|
|
// Returns zero if the library has been loaded, -1 in case of error.
|
|
|
|
|
//-
|
|
|
|
|
int net_vde_prepare(void) {
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
#error VDE is not supported in Windows
|
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
|
libvde_handle = dynld_module("libvdeplug.dylib", vde_imports);
|
|
|
|
|
#else
|
|
|
|
|
libvde_handle = dynld_module("libvdeplug.so", vde_imports);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (libvde_handle == NULL) {
|
|
|
|
|
vde_log("VDE: error loading VDEPLUG module\n");
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
network_devmap.has_vde = 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// Close a VDE socket connection
|
|
|
|
|
//-
|
|
|
|
|
void net_vde_close(void *priv) {
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!priv) return;
|
|
|
|
|
|
|
|
|
|
net_vde_t *vde = (net_vde_t *) priv;
|
|
|
|
|
vde_log("VDE: closing.\n");
|
|
|
|
|
net_event_set(&vde->stop_event); // Tell the thread to finish
|
|
|
|
|
vde_log("VDE: Waiting for the thread to finish...\n");
|
|
|
|
|
thread_wait(vde->poll_tid);
|
|
|
|
|
vde_log("VDE: Thread finished.\n");
|
|
|
|
|
|
|
|
|
|
// Free all the mallocs!
|
|
|
|
|
for(i=0;i<VDE_PKT_BATCH; i++) {
|
|
|
|
|
free(vde->pktv[i].data);
|
|
|
|
|
}
|
|
|
|
|
free(vde->pkt.data);
|
|
|
|
|
f_vde_close((void *) vde->vdeconn);
|
|
|
|
|
net_event_close(&vde->tx_event);
|
|
|
|
|
net_event_close(&vde->stop_event);
|
|
|
|
|
free(vde);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// Signal packets are available to be transmitted
|
|
|
|
|
//-
|
|
|
|
|
void net_vde_in_available(void *priv) {
|
|
|
|
|
net_vde_t *vde = (net_vde_t *) priv;
|
|
|
|
|
net_event_set(&vde->tx_event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// Initialize VDE for use
|
|
|
|
|
// At this point the vdeplug library is already loaded
|
|
|
|
|
// card: network card we are attaching
|
|
|
|
|
// mac_addr: MAC address we are using
|
|
|
|
|
// priv: Name of the VDE contol socket directory
|
|
|
|
|
//-
|
|
|
|
|
void *net_vde_init(const netcard_t *card, const uint8_t *mac_addr, void *priv) {
|
|
|
|
|
struct vde_open_args vde_args;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
char *socket_name = (char *) priv;
|
|
|
|
|
|
|
|
|
|
if (libvde_handle == NULL) {
|
|
|
|
|
vde_log("VDE: net_vde_init without library handle!\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((socket_name[0] == '\0') || !strcmp(socket_name, "none")) {
|
|
|
|
|
vde_log("VDE: No socket name configured!\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vde_log("VDE: Attaching to virtual switch at %s\n", socket_name);
|
|
|
|
|
|
|
|
|
|
net_vde_t *vde = calloc(1, sizeof(net_vde_t));
|
|
|
|
|
vde->card = (netcard_t *) card;
|
|
|
|
|
memcpy(vde->mac_addr, mac_addr, sizeof(vde->mac_addr));
|
|
|
|
|
|
|
|
|
|
vde_args.group = 0;
|
|
|
|
|
vde_args.port = 0;
|
|
|
|
|
vde_args.mode = 0;
|
|
|
|
|
|
|
|
|
|
// We are calling vde_open_real(), not the vde_open() macro...
|
|
|
|
|
if ((vde->vdeconn = f_vde_open(socket_name, VDE_DESCRIPTION,
|
|
|
|
|
LIBVDEPLUG_INTERFACE_VERSION, &vde_args)) == NULL) {
|
|
|
|
|
vde_log("VDE: Unable to open socket %s (%s)!\n", socket_name, strerror(errno));
|
|
|
|
|
free(vde);
|
|
|
|
|
//+
|
|
|
|
|
// There is a bug upstream that causes an uncontrolled crash if the network is not
|
|
|
|
|
// properly initialized.
|
|
|
|
|
// To avoid that crash, we tell the user we cannot continue and exit the program.
|
|
|
|
|
// TODO: Once there is a solution for the mentioned crash, this should be removed
|
|
|
|
|
// and/or replaced by proper error handling code.
|
|
|
|
|
//-
|
2023-05-16 00:53:59 +02:00
|
|
|
// fatal("Could not open the specified VDE socket (%s). Please fix your networking configuration.", socket_name);
|
2023-05-04 21:12:02 +02:00
|
|
|
// It makes no sense to issue this warning since the program will crash anyway...
|
|
|
|
|
// ui_msgbox_header(MBX_WARNING, (wchar_t *) IDS_2167, (wchar_t *) IDS_2168);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
vde_log("VDE: Socket opened (%s).\n", socket_name);
|
|
|
|
|
|
|
|
|
|
for(i=0; i < VDE_PKT_BATCH; i++) {
|
|
|
|
|
vde->pktv[i].data = calloc(1, NET_MAX_FRAME);
|
|
|
|
|
}
|
|
|
|
|
vde->pkt.data = calloc(1,NET_MAX_FRAME);
|
|
|
|
|
net_event_init(&vde->tx_event);
|
|
|
|
|
net_event_init(&vde->stop_event);
|
|
|
|
|
vde->poll_tid = thread_create(net_vde_thread, vde); // Fire up the read-write thread!
|
|
|
|
|
|
|
|
|
|
return vde;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//+
|
|
|
|
|
// VDE Driver structure
|
|
|
|
|
//-
|
|
|
|
|
const netdrv_t net_vde_drv = {
|
|
|
|
|
&net_vde_in_available,
|
|
|
|
|
&net_vde_init,
|
|
|
|
|
&net_vde_close,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|