From 52b0e54fd71f611af2a2a442375f9b933dbcddcf Mon Sep 17 00:00:00 2001 From: Jordi Guillaumes i Pons Date: Thu, 4 May 2023 21:12:02 +0200 Subject: [PATCH] VDE networking support Adds VDE (https://github.com/virtualsquare/vde-2) support to 86Box. - Adds new networking type - Modifies networking configuration dialog - Adds CMake Presets to build and debug under MacOS --- .gitignore | 3 + CMakePresets.json | 66 +++++++ src/config.c | 52 ++++-- src/include/86box/network.h | 18 +- src/network/CMakeLists.txt | 14 +- src/network/net_vde.c | 310 +++++++++++++++++++++++++++++++ src/network/network.c | 35 +++- src/qt/qt_settingsnetwork.cpp | 46 +++-- src/qt/qt_settingsnetwork.ui | 338 +++++++++++++++++++--------------- 9 files changed, 701 insertions(+), 181 deletions(-) create mode 100644 src/network/net_vde.c diff --git a/.gitignore b/.gitignore index acb28baf7..48a5950b9 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ CMakeLists.txt.user /debian/debhelper-build-stamp /debian/files /obj-*-linux-gnu + +# MacOS Finder stuff +.DS_Store diff --git a/CMakePresets.json b/CMakePresets.json index d2980ed87..0dbaf1988 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -56,6 +56,72 @@ "NEW_DYNAREC": "ON" }, "inherits": "base" + }, + { + "name": "llvm-macos-aarch64.cmake", + "displayName": "MacOS clang regular", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "cmake/llvm-macos-aarch64.cmake", + "NEW_DYNAREC": "ON", + "QT": "ON", + "USE_QT6": "OFF", + "Qt5_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5", + "MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk", + "Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools", + "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft" + }, + "inherits": "regular" + }, + { + "name": "llvm-macos-aarch64-debug", + "displayName": "MacOS clang debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "cmake/llvm-macos-aarch64.cmake", + "NEW_DYNAREC": "ON", + "QT": "ON", + "USE_QT6": "OFF", + "Qt5_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5", + "MOLTENVK_DIR": "/opt/homebrew/opt/molten-vk", + "Qt5LinguistTools_DIR": "/opt/homebrew/opt/qt@5/lib/cmake/Qt5LinguistTools", + "OpenAL_ROOT": "/opt/homebrew/opt/openal-soft", + "CMAKE_CXX_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG", + "CMAKE_C_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG" + }, + "inherits": "debug" + }, + { + "name": "flags-gcc-aarch64-debug", + "displayName": "Linux ARM 64 - Debug", + "description": "Linux ARM64 - Debug build", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "NEW_DYNAREC": "ON", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "cmake/flags-gcc-aarch64.cmake", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", + "CMAKE_CXX_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG", + "CMAKE_C_FLAGS_DEBUG": "-g -O0 -DENABLE_VDE_LOG" + }, + "inherits": "debug" + }, + { + "name": "flags-gcc-aarch64-regular", + "displayName": "Linux ARM 64 - Regular", + "description": "Linux ARM64 - Release build", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "NEW_DYNAREC": "ON", + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_TOOLCHAIN_FILE": "cmake/flags-gcc-aarch64.cmake", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" + }, + "inherits": "regular" } ], "buildPresets": [], diff --git a/src/config.c b/src/config.c index 75a3973e4..f9570442b 100644 --- a/src/config.c +++ b/src/config.c @@ -783,6 +783,8 @@ load_network(void) net_cards_conf[c].net_type = NET_TYPE_PCAP; else if (!strcmp(p, "slirp") || !strcmp(p, "2")) net_cards_conf[c].net_type = NET_TYPE_SLIRP; + else if (!strcmp(p, "vde") || !strcmp(p, "2")) + net_cards_conf[c].net_type = NET_TYPE_VDE; else net_cards_conf[c].net_type = NET_TYPE_NONE; } else { @@ -791,13 +793,17 @@ load_network(void) p = ini_section_get_string(cat, "net_host_device", NULL); if (p != NULL) { - if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) { - if (network_ndev == 1) { - ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2130); - } else if (network_dev_to_id(p) == -1) { - ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2096, (wchar_t *) IDS_2130); + if (net_cards_conf[c].net_type == NET_TYPE_PCAP) { + if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) { + if (network_ndev == 1) { + ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2130); + } else if (network_dev_to_id(p) == -1) { + ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2096, (wchar_t *) IDS_2130); + } + strcpy(net_cards_conf[c].host_dev_name, "none"); + } else { + strncpy(net_cards_conf[c].host_dev_name, p, sizeof(net_cards_conf[c].host_dev_name) - 1); } - strcpy(net_cards_conf[c].host_dev_name, "none"); } else { strncpy(net_cards_conf[c].host_dev_name, p, sizeof(net_cards_conf[c].host_dev_name) - 1); } @@ -828,6 +834,8 @@ load_network(void) net_cards_conf[c].net_type = NET_TYPE_PCAP; } else if (!strcmp(p, "slirp") || !strcmp(p, "2")) { net_cards_conf[c].net_type = NET_TYPE_SLIRP; + } else if (!strcmp(p, "vde") || !strcmp(p, "2")) { + net_cards_conf[c].net_type = NET_TYPE_VDE; } else { net_cards_conf[c].net_type = NET_TYPE_NONE; } @@ -838,13 +846,15 @@ load_network(void) sprintf(temp, "net_%02i_host_device", c + 1); p = ini_section_get_string(cat, temp, NULL); if (p != NULL) { - if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) { - if (network_ndev == 1) { - ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2130); - } else if (network_dev_to_id(p) == -1) { - ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2096, (wchar_t *) IDS_2130); + if (net_cards_conf[c].net_type == NET_TYPE_PCAP) { + if ((network_dev_to_id(p) == -1) || (network_ndev == 1)) { + if (network_ndev == 1) { + ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2095, (wchar_t *) IDS_2130); + } else if (network_dev_to_id(p) == -1) { + ui_msgbox_header(MBX_ERROR, (wchar_t *) IDS_2096, (wchar_t *) IDS_2130); + } + strcpy(net_cards_conf[c].host_dev_name, "none"); } - strcpy(net_cards_conf[c].host_dev_name, "none"); } else { strncpy(net_cards_conf[c].host_dev_name, p, sizeof(net_cards_conf[c].host_dev_name) - 1); } @@ -2469,11 +2479,19 @@ save_network(void) } sprintf(temp, "net_%02i_net_type", c + 1); - if (net_cards_conf[c].net_type == NET_TYPE_NONE) { - ini_section_delete_var(cat, temp); - } else { - ini_section_set_string(cat, temp, - (net_cards_conf[c].net_type == NET_TYPE_SLIRP) ? "slirp" : "pcap"); + switch(net_cards_conf[c].net_type) { + case NET_TYPE_NONE: + ini_section_delete_var(cat, temp); + break; + case NET_TYPE_SLIRP: + ini_section_set_string(cat, temp, "slirp"); + break; + case NET_TYPE_PCAP: + ini_section_set_string(cat, temp, "pcap"); + break; + case NET_TYPE_VDE: + ini_section_set_string(cat, temp, "vde"); + break; } sprintf(temp, "net_%02i_host_device", c + 1); diff --git a/src/include/86box/network.h b/src/include/86box/network.h index 0a396968f..4f514d1ac 100644 --- a/src/include/86box/network.h +++ b/src/include/86box/network.h @@ -51,6 +51,7 @@ #define NET_TYPE_NONE 0 /* networking disabled */ #define NET_TYPE_SLIRP 1 /* use the SLiRP port forwarder */ #define NET_TYPE_PCAP 2 /* use the (Win)Pcap API */ +#define NET_TYPE_VDE 3 /* use the VDE plug API */ #define NET_MAX_FRAME 1518 /* Queue size must be a power of 2 */ @@ -123,6 +124,7 @@ typedef struct netdrv_t { extern const netdrv_t net_pcap_drv; extern const netdrv_t net_slirp_drv; +extern const netdrv_t net_vde_drv; struct _netcard_t { const device_t *device; @@ -147,15 +149,27 @@ typedef struct { char description[128]; } netdev_t; +typedef struct { + int has_slirp: 1; + int has_pcap: 1; + int has_vde: 1; +} network_devmap_t; + + +#define HAS_NOSLIRP_NET(x) (x.has_pcap || x.has_vde) + #ifdef __cplusplus extern "C" { #endif /* Global variables. */ extern int nic_do_log; /* config */ -extern int network_ndev; +extern network_devmap_t network_devmap; +extern int network_ndev; // Number of pcap devices +extern network_devmap_t network_devmap; // Bitmap of available network types extern netdev_t network_devs[NET_HOST_INTF_MAX]; + /* Function prototypes. */ extern void network_init(void); extern netcard_t *network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETSETLINKSTATE set_link_state); @@ -166,6 +180,8 @@ extern int network_available(void); extern void network_tx(netcard_t *card, uint8_t *, int); extern int net_pcap_prepare(netdev_t *); +extern int net_vde_prepare(void); + extern void network_connect(int id, int connect); extern int network_is_connected(int id); diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index e14f979d3..c6178a790 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -12,8 +12,8 @@ # # Copyright 2020-2021 David Hrdlička. # - -add_library(net OBJECT network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c +set(net_sources) +list(APPEND net_sources network.c net_pcap.c net_slirp.c net_dp8390.c net_3c501.c net_3c503.c net_ne2000.c net_pcnet.c net_wd8003.c net_plip.c net_event.c) option(SLIRP_EXTERNAL "Link against the system-provided libslirp library" OFF) @@ -39,3 +39,13 @@ endif() if(WIN32) target_link_libraries(86Box ws2_32) endif() + +if (UNIX) + find_path(HAS_VDE "libvdeplug.h" PATHS ${VDE_INCLUDE_DIR} "/usr/include /usr/local/include" "/opt/homebrew/include" ) + if(HAS_VDE) + add_compile_definitions(HAS_VDE) + list(APPEND net_sources net_vde.c) + endif() +endif() + +add_library(net OBJECT ${net_sources}) diff --git a/src/network/net_vde.c b/src/network/net_vde.c new file mode 100644 index 000000000..904b2789a --- /dev/null +++ b/src/network/net_vde.c @@ -0,0 +1,310 @@ + /* + * 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 + * Copyright 2023 jguillaumes. + * + * See the COPYING file at the top of the 86box for license details. + * TL;DR: GPL version 2. + */ +#include +#include +#include +#include +#include + +#if !defined(_WIN32) +#include +#include +#else +#error VDE is not supported under windows +#endif + +#include + +#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 +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; ivdeconn, 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;ipktv[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. + //- + fatal("Could not open the specified VDE socket (%s). Please fix your networking configuration.", socket_name); + // 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 +}; + diff --git a/src/network/network.c b/src/network/network.c index f6e9eb565..ce81fcc93 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -123,7 +123,8 @@ netcard_conf_t net_cards_conf[NET_CARD_MAX]; uint16_t net_card_current = 0; /* Global variables. */ -int network_ndev; +network_devmap_t network_devmap; +int network_ndev; netdev_t network_devs[NET_HOST_INTF_MAX]; /* Local variables. */ @@ -214,9 +215,20 @@ network_init(void) network_ndev = 1; /* Initialize the Pcap system module, if present. */ + + network_devmap.has_slirp = 1; i = net_pcap_prepare(&network_devs[network_ndev]); - if (i > 0) + if (i > 0) { + network_devmap.has_pcap = 1; network_ndev += i; + } + +#ifdef HAS_VDE + // Try to load the VDE plug library + if(net_vde_prepare()==0) { + network_devmap.has_vde = 1; + } +#endif #if defined ENABLE_NETWORK_LOG && !defined(_WIN32) /* Start packet dump. */ @@ -286,6 +298,17 @@ int network_queue_put_swap(netqueue_t *queue, netpkt_t *src_pkt) { if (src_pkt->len == 0 || src_pkt->len > NET_MAX_FRAME || network_queue_full(queue)) { +#ifdef DEBUG + if (src_pkt->len == 0) { + network_log("Discarded zero length packet.\n"); + } else if (src_pkt->len > NET_MAX_FRAME) { + network_log("Discarded oversized packet of len=%d.\n", src_pkt->len); + network_dump_packet(src_pkt); + } else { + network_log("Discarded %d bytes packet because the queue is full.\n", src_pkt->len); + network_dump_packet(src_pkt); + } +#endif return 0; } @@ -434,6 +457,12 @@ network_attach(void *card_drv, uint8_t *mac, NETRXCB rx, NETSETLINKSTATE set_lin card->host_drv = net_pcap_drv; card->host_drv.priv = card->host_drv.init(card, mac, net_cards_conf[net_card_current].host_dev_name); break; +#ifdef HAS_VDE + case NET_TYPE_VDE: + card->host_drv = net_vde_drv; + card->host_drv.priv = card->host_drv.init(card, mac, net_cards_conf[net_card_current].host_dev_name); + break; +#endif } if (!card->host_drv.priv) { @@ -617,6 +646,8 @@ network_dev_available(int id) if ((net_cards_conf[id].net_type == NET_TYPE_PCAP && (network_dev_to_id(net_cards_conf[id].host_dev_name) <= 0))) available = 0; + // TODO: Handle VDE device + return available; } diff --git a/src/qt/qt_settingsnetwork.cpp b/src/qt/qt_settingsnetwork.cpp index 014e82ab3..7134e3381 100644 --- a/src/qt/qt_settingsnetwork.cpp +++ b/src/qt/qt_settingsnetwork.cpp @@ -37,13 +37,17 @@ SettingsNetwork::enableElements(Ui::SettingsNetwork *ui) auto *net_type_cbox = findChild(QString("comboBoxNet%1").arg(i + 1)); auto *intf_cbox = findChild(QString("comboBoxIntf%1").arg(i + 1)); auto *conf_btn = findChild(QString("pushButtonConf%1").arg(i + 1)); + auto *socket_line = findChild(QString("socketVDENIC%1").arg(i + 1)); int netType = net_type_cbox->currentData().toInt(); - bool adaptersEnabled = netType == NET_TYPE_SLIRP || (netType == NET_TYPE_PCAP && intf_cbox->currentData().toInt() > 0); + bool adaptersEnabled = netType == NET_TYPE_SLIRP + || NET_TYPE_VDE + || (netType == NET_TYPE_PCAP && intf_cbox->currentData().toInt() > 0); intf_cbox->setEnabled(net_type_cbox->currentData().toInt() == NET_TYPE_PCAP); nic_cbox->setEnabled(adaptersEnabled); conf_btn->setEnabled(adaptersEnabled && network_card_has_config(nic_cbox->currentData().toInt())); + socket_line->setEnabled(net_type_cbox->currentData().toInt() == NET_TYPE_VDE); } } @@ -59,6 +63,7 @@ SettingsNetwork::SettingsNetwork(QWidget *parent) auto *nic_cbox = findChild(QString("comboBoxNIC%1").arg(i + 1)); auto *net_type_cbox = findChild(QString("comboBoxNet%1").arg(i + 1)); auto *intf_cbox = findChild(QString("comboBoxIntf%1").arg(i + 1)); + auto *socket_line = findChild(QString("socketVDENIC%1").arg(i + 1)); connect(nic_cbox, QOverload::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged); connect(net_type_cbox, QOverload::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged); connect(intf_cbox, QOverload::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged); @@ -75,12 +80,18 @@ SettingsNetwork::save() { for (int i = 0; i < NET_CARD_MAX; ++i) { auto *cbox = findChild(QString("comboBoxNIC%1").arg(i + 1)); + auto *socket_line = findChild(QString("socketVDENIC%1").arg(i + 1)); net_cards_conf[i].device_num = cbox->currentData().toInt(); cbox = findChild(QString("comboBoxNet%1").arg(i + 1)); net_cards_conf[i].net_type = cbox->currentData().toInt(); cbox = findChild(QString("comboBoxIntf%1").arg(i + 1)); memset(net_cards_conf[i].host_dev_name, '\0', sizeof(net_cards_conf[i].host_dev_name)); - strncpy(net_cards_conf[i].host_dev_name, network_devs[cbox->currentData().toInt()].device, sizeof(net_cards_conf[i].host_dev_name) - 1); + if (net_cards_conf[i].net_type == NET_TYPE_PCAP) { + strncpy(net_cards_conf[i].host_dev_name, network_devs[cbox->currentData().toInt()].device, sizeof(net_cards_conf[i].host_dev_name) - 1); + } else if (net_cards_conf[i].net_type == NET_TYPE_VDE) { + const char *str_socket = socket_line->text().toStdString().c_str(); + strncpy(net_cards_conf[i].host_dev_name, str_socket, strlen(str_socket)); + } } } @@ -124,26 +135,37 @@ SettingsNetwork::onCurrentMachineChanged(int machineId) removeRows = model->rowCount(); Models::AddEntry(model, tr("None"), NET_TYPE_NONE); Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP); + if (network_ndev > 1) { Models::AddEntry(model, "PCap", NET_TYPE_PCAP); } + if (network_devmap.has_vde) { + Models::AddEntry(model, "VDE", NET_TYPE_VDE); + } + model->removeRows(0, removeRows); cbox->setCurrentIndex(net_cards_conf[i].net_type); selectedRow = 0; - QString currentPcapDevice = net_cards_conf[i].host_dev_name; - cbox = findChild(QString("comboBoxIntf%1").arg(i + 1)); - model = cbox->model(); - removeRows = model->rowCount(); - for (int c = 0; c < network_ndev; c++) { - Models::AddEntry(model, tr(network_devs[c].description), c); - if (QString(network_devs[c].device) == currentPcapDevice) { - selectedRow = c; + if (net_cards_conf[i].net_type == NET_TYPE_PCAP) { + QString currentPcapDevice = net_cards_conf[i].host_dev_name; + cbox = findChild(QString("comboBoxIntf%1").arg(i + 1)); + model = cbox->model(); + removeRows = model->rowCount(); + for (int c = 0; c < network_ndev; c++) { + Models::AddEntry(model, tr(network_devs[c].description), c); + if (QString(network_devs[c].device) == currentPcapDevice) { + selectedRow = c; + } } + model->removeRows(0, removeRows); + cbox->setCurrentIndex(selectedRow); + } else if (net_cards_conf[i].net_type == NET_TYPE_VDE) { + QString currentVdeSocket = net_cards_conf[i].host_dev_name; + auto editline = findChild(QString("socketVDENIC%1").arg(i+1)); + editline->setText(currentVdeSocket); } - model->removeRows(0, removeRows); - cbox->setCurrentIndex(selectedRow); } } diff --git a/src/qt/qt_settingsnetwork.ui b/src/qt/qt_settingsnetwork.ui index d781a1beb..d6f77a0a2 100644 --- a/src/qt/qt_settingsnetwork.ui +++ b/src/qt/qt_settingsnetwork.ui @@ -36,19 +36,6 @@ Network Card #1 - - - - - 0 - 0 - - - - QComboBox::AdjustToContents - - - @@ -62,14 +49,17 @@ - - + + 0 0 + + QComboBox::AdjustToContents + @@ -98,17 +88,14 @@ - - + + - + 0 0 - - Mode - @@ -121,7 +108,7 @@ - + Qt::Vertical @@ -134,6 +121,33 @@ + + + + + 0 + 0 + + + + Mode + + + + + + + 127 + + + + + + + VDE Socket + + + @@ -141,6 +155,36 @@ Network Card #2 + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + + + + + Configure + + + @@ -154,19 +198,6 @@ - - - - - 0 - 0 - - - - Mode - - - @@ -193,6 +224,19 @@ + + + + + 0 + 0 + + + + Mode + + + @@ -203,25 +247,28 @@ - - - - - 0 - 0 - - - - - - + + - Configure + VDE Socket - - + + + + + + + + Network Card #3 + + + + + + + Qt::Vertical @@ -233,23 +280,6 @@ - - - - - Network Card #3 - - - - - - - 0 - 0 - - - - @@ -263,17 +293,14 @@ - - + + - + 0 0 - - Mode - @@ -299,19 +326,6 @@ - - - - - 0 - 0 - - - - Configure - - - @@ -325,41 +339,8 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Network Card #4 - - - - - - - 0 - 0 - - - - QComboBox::AdjustToContents - - - - + 0 @@ -371,14 +352,44 @@ - - + + + + VDE Socket + + + + + - + 0 0 + + Mode + + + + + + + + Network Card #4 + + + + + + + 0 + 0 + + + + Adapter + @@ -394,29 +405,6 @@ - - - - - 0 - 0 - - - - Adapter - - - - - - - - 0 - 0 - - - - @@ -430,7 +418,43 @@ - + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Configure + + + + Qt::Vertical @@ -443,6 +467,26 @@ + + + + + 0 + 0 + + + + + + + + + + + VDE Socket + + +