Files
86Box/src/network/net_vde.c
redoste a947e49f5e Allow connection to a VDE switch run by the same user
`libvdeplug` will only change the mode if the switch is run by another
user that isn't root.
If the switch is run by the same user as 86Box the socket will be
chmoded to 000 and the switch, unable to connect back, will drop the
connection.

187256c528/libvdeplug4/libvdeplug_vde.c (L261-L275)
2023-09-05 19:04:46 +02:00

311 lines
8.9 KiB
C

/*
* 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 net_vde_t {
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(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);
}
//+
// Copy error message to the error buffer
// and log if enabled.
//-
void net_vde_error(char *errbuf, const char *message) {
strncpy(errbuf, message, NET_DRV_ERRBUF_SIZE);
vde_log("VDE: %s\n", message);
}
//+
// 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, char *netdrv_errbuf) {
struct vde_open_args vde_args;
char *socket_name = (char *) priv;
if (libvde_handle == NULL) {
net_vde_error(netdrv_errbuf, "net_vde_init without library handle");
return NULL;
}
if ((socket_name[0] == '\0') || !strcmp(socket_name, "none")) {
net_vde_error(netdrv_errbuf, "No socket name configured");
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 = 0700; // Allow the switch to connect back to our socket if it is run by the same user
// 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) {
char buf[NET_DRV_ERRBUF_SIZE];
snprintf(buf, NET_DRV_ERRBUF_SIZE, "Unable to open socket %s (%s)", socket_name, strerror(errno));
net_vde_error(netdrv_errbuf, buf);
free(vde);
return NULL;
}
vde_log("VDE: Socket opened (%s).\n", socket_name);
for(uint8_t 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
};