mirror of
https://github.com/86Box/86Box.git
synced 2026-02-24 02:18:20 -07:00
552 lines
20 KiB
C
552 lines
20 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.
|
|
*
|
|
* Network Switch network driver.
|
|
*
|
|
* Authors: RichardG, <richardg867@gmail.com>
|
|
*
|
|
* Copyright 2026 RichardG.
|
|
*/
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <wchar.h>
|
|
#include <fcntl.h>
|
|
#ifdef _WIN32
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <winsock2.h>
|
|
# include <ws2tcpip.h>
|
|
# include <windows.h>
|
|
# include <iphlpapi.h>
|
|
# define IFF_POINTOPOINT IFF_POINTTOPOINT
|
|
#else
|
|
# include <unistd.h>
|
|
# include <poll.h>
|
|
# include <sys/types.h>
|
|
# include <sys/socket.h>
|
|
# include <netinet/in.h>
|
|
# include <arpa/inet.h>
|
|
# include <ifaddrs.h>
|
|
# include <net/if.h>
|
|
#endif
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include <86box/device.h>
|
|
#include <86box/plat.h>
|
|
#include <86box/thread.h>
|
|
#include <86box/timer.h>
|
|
#include <86box/network.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/ini.h>
|
|
#include <86box/config.h>
|
|
#include <86box/net_event.h>
|
|
#include <86box/bswap.h>
|
|
|
|
#define SWITCH_PKT_BATCH NET_QUEUE_LEN
|
|
|
|
#define SWITCH_MULTICAST_GROUP 0xefff5656 /* 239.255.86.86 */
|
|
#define SWITCH_MULTICAST_PORT 8086
|
|
|
|
enum {
|
|
NET_EVENT_STOP = 0,
|
|
NET_EVENT_TX,
|
|
NET_EVENT_RX,
|
|
NET_EVENT_MAX
|
|
};
|
|
|
|
typedef union {
|
|
struct sockaddr sa;
|
|
struct sockaddr_in sin;
|
|
struct sockaddr_in6 sin6;
|
|
} net_switch_sockaddr_t;
|
|
|
|
typedef struct net_switch_hostaddr_t {
|
|
struct net_switch_hostaddr_t *next;
|
|
net_switch_sockaddr_t addr;
|
|
net_switch_sockaddr_t addr_tx;
|
|
int socket_tx;
|
|
} net_switch_hostaddr_t;
|
|
|
|
typedef struct net_switch_t {
|
|
int socket_rx;
|
|
net_switch_hostaddr_t *hostaddrs;
|
|
uint16_t port_out;
|
|
|
|
uint8_t promisc;
|
|
union {
|
|
uint8_t mac_addr[6];
|
|
uint64_t mac_addr_u64;
|
|
};
|
|
netcard_t * card; /* netcard attached to us */
|
|
thread_t * poll_tid;
|
|
net_evt_t tx_event;
|
|
net_evt_t stop_event;
|
|
netpkt_t pkt;
|
|
netpkt_t pkt_tx_v[SWITCH_PKT_BATCH];
|
|
int during_tx;
|
|
int recv_on_tx;
|
|
#ifdef _WIN32
|
|
HANDLE sock_event;
|
|
#endif
|
|
} net_switch_t;
|
|
|
|
#ifdef ENABLE_SWITCH_LOG
|
|
int switch_do_log = ENABLE_SWITCH_LOG;
|
|
|
|
static void
|
|
netswitch_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (switch_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define netswitch_log(fmt, ...)
|
|
#endif
|
|
|
|
static void
|
|
net_switch_in_available(void *priv)
|
|
{
|
|
net_switch_t *netswitch = (net_switch_t *) priv;
|
|
net_event_set(&netswitch->tx_event);
|
|
}
|
|
|
|
static unsigned int
|
|
net_switch_add_hostaddr(net_switch_t *netswitch, net_switch_sockaddr_t *addr, net_switch_sockaddr_t *broadcast, net_switch_sockaddr_t *netmask, unsigned int flags)
|
|
{
|
|
if (!addr || !(flags & IFF_UP))
|
|
return 0;
|
|
|
|
/* Iterate to the end of the list. */
|
|
net_switch_hostaddr_t **p = &netswitch->hostaddrs;
|
|
for (; *p; p = &(*p)->next) {
|
|
/* Check for duplicates. */
|
|
switch (addr->sa.sa_family) {
|
|
case AF_INET:
|
|
if ((*p)->addr.sin.sin_addr.s_addr == addr->sin.sin_addr.s_addr)
|
|
return 0;
|
|
break;
|
|
#ifdef UNUSED
|
|
case AF_INET6:
|
|
if (AS_U64((*p)->addr.sin6.sin6_addr.s6_addr[0]) == AS_U64(addr->sin6.sin6_addr.s6_addr[0]) &&
|
|
AS_U64((*p)->addr.sin6.sin6_addr.s6_addr[8]) == AS_U64(addr->sin6.sin6_addr.s6_addr[8]))
|
|
return 0;
|
|
break;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Handle address. */
|
|
net_switch_hostaddr_t *hostaddr = (net_switch_hostaddr_t *) calloc(1, sizeof(net_switch_hostaddr_t));
|
|
hostaddr->socket_tx = -1;
|
|
unsigned int ret = 1;
|
|
if (addr->sa.sa_family == AF_INET) {
|
|
#ifdef ENABLE_SWITCH_LOG
|
|
char buf[INET_ADDRSTRLEN];
|
|
buf[0] = '\0';
|
|
inet_ntop(addr->sin.sin_family, &addr->sin.sin_addr.s_addr, buf, sizeof(buf));
|
|
#endif
|
|
|
|
/* Initialize transmit socket for this interface. */
|
|
hostaddr->socket_tx = socket(addr->sin.sin_family, SOCK_DGRAM, 0);
|
|
if (hostaddr->socket_tx < 0) {
|
|
netswitch_log("Network Switch: could not initialize transmit socket for interface %s\n", buf);
|
|
goto fail;
|
|
}
|
|
|
|
/* Initialize addresses. */
|
|
memcpy(&hostaddr->addr.sin, &addr->sin, sizeof(struct sockaddr_in));
|
|
hostaddr->addr_tx.sin.sin_family = addr->sin.sin_family;
|
|
hostaddr->addr_tx.sin.sin_port = netswitch->port_out;
|
|
|
|
/* The problem with multicasting through multiple interfaces is loopback, in which
|
|
all copies of the datagram get reflected back to us and to other instances on the
|
|
same host. Disabling IP_MULTICAST_LOOP on all but one transmit socket can mitigate
|
|
that, but not on Windows where that option applies to the receive socket, so we
|
|
instead disable loopback on all multicast sockets and use a broadcast for loopback. */
|
|
if ((flags & (IFF_MULTICAST | IFF_LOOPBACK)) == IFF_MULTICAST) {
|
|
/* Set multicast interface for the transmit socket. */
|
|
if (setsockopt(hostaddr->socket_tx, IPPROTO_IP, IP_MULTICAST_IF, (char *) &hostaddr->addr.sin.sin_addr.s_addr, sizeof(hostaddr->addr.sin.sin_addr.s_addr)) < 0) {
|
|
netswitch_log("Network Switch: could not configure multicast on interface %s\n", buf);
|
|
goto broadcast;
|
|
}
|
|
|
|
/* Join IPv4 multicast group. */
|
|
struct ip_mreq mreq = {
|
|
.imr_multiaddr = { .s_addr = htonl(SWITCH_MULTICAST_GROUP) },
|
|
.imr_interface = { .s_addr = hostaddr->addr.sin.sin_addr.s_addr }
|
|
};
|
|
if (setsockopt(netswitch->socket_rx, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq)) < 0) {
|
|
netswitch_log("Network Switch: could not join multicast group on interface %s\n", buf);
|
|
goto broadcast;
|
|
}
|
|
|
|
/* Destination address is multicast to our group. */
|
|
hostaddr->addr_tx.sin.sin_addr.s_addr = mreq.imr_multiaddr.s_addr;
|
|
|
|
/* Disable multicast loopback on non-Windows platforms. (no harm on Windows) */
|
|
int val = 0;
|
|
setsockopt(hostaddr->socket_tx, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &val, sizeof(val));
|
|
|
|
netswitch_log("Network Switch: added multicast interface %s", buf);
|
|
} else {
|
|
broadcast:
|
|
/* Determine destination address. */
|
|
if (flags & IFF_LOOPBACK) {
|
|
/* Loopback interfaces don't advertise broadcast support and therefore
|
|
the broadcast address is invalid, so we build one from the netmask. */
|
|
hostaddr->addr_tx.sin.sin_addr.s_addr = hostaddr->addr.sin.sin_addr.s_addr | (netmask ? ~netmask->sin.sin_addr.s_addr : htonl(0x00ffffff));
|
|
ret = 0;
|
|
} else if (!(flags & (IFF_BROADCAST | IFF_POINTOPOINT)) ||
|
|
!broadcast || !broadcast->sin.sin_addr.s_addr ||
|
|
(broadcast->sin.sin_addr.s_addr == hostaddr->addr.sin.sin_addr.s_addr)) {
|
|
/* This interface is unicast-only or P2P with a bad peer address, nothing we can do. */
|
|
netswitch_log("Network Switch: ignored %s interface %s\n", (flags & (IFF_LOOPBACK | IFF_BROADCAST)) ? "broadcast" : "unicast", buf);
|
|
goto fail;
|
|
} else {
|
|
/* Valid broadcast/peer address. */
|
|
hostaddr->addr_tx.sin.sin_addr.s_addr = broadcast->sin.sin_addr.s_addr;
|
|
}
|
|
|
|
/* Enable broadcast on the transmit socket if required. */
|
|
if (flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
|
|
int val = 1;
|
|
if (setsockopt(hostaddr->socket_tx, SOL_SOCKET, SO_BROADCAST, (char *) &val, sizeof(val)) < 0) {
|
|
netswitch_log("Network Switch: could not configure broadcast on interface %s\n", buf);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
netswitch_log("Network Switch: added %s interface %s", (flags & (IFF_LOOPBACK | IFF_BROADCAST)) ? "broadcast" : "unicast", buf);
|
|
}
|
|
#ifdef ENABLE_SWITCH_LOG
|
|
buf[0] = '\0';
|
|
inet_ntop(hostaddr->addr_tx.sin.sin_family, &hostaddr->addr_tx.sin.sin_addr.s_addr, buf, sizeof(buf));
|
|
netswitch_log(" -> %s:%d\n", buf, ntohs(netswitch->port_out));
|
|
#endif
|
|
} else {
|
|
goto fail;
|
|
}
|
|
|
|
/* Add address to list. */
|
|
*p = hostaddr;
|
|
return ret;
|
|
|
|
fail:
|
|
if (hostaddr->socket_tx >= 0)
|
|
close(hostaddr->socket_tx);
|
|
free(hostaddr);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
net_switch_update_hostaddrs(net_switch_t *netswitch)
|
|
{
|
|
unsigned int added = 0;
|
|
#ifdef _WIN32
|
|
DWORD buf_size = 16 * sizeof(INTERFACE_INFO);
|
|
INTERFACE_INFO *buf;
|
|
retry:
|
|
buf = (INTERFACE_INFO *) malloc(buf_size);
|
|
DWORD returned;
|
|
if (WSAIoctl(netswitch->socket_rx, SIO_GET_INTERFACE_LIST, NULL, 0, buf, buf_size, &returned, NULL, NULL) == SOCKET_ERROR) {
|
|
free(buf);
|
|
if (WSAGetLastError() == WSAEFAULT) {
|
|
buf_size *= 2;
|
|
goto retry;
|
|
}
|
|
} else {
|
|
returned /= sizeof(INTERFACE_INFO);
|
|
for (int i = 0; i < returned; i++) {
|
|
added += net_switch_add_hostaddr(netswitch,
|
|
(net_switch_sockaddr_t *) &buf[i].iiAddress.Address,
|
|
(net_switch_sockaddr_t *) &buf[i].iiBroadcastAddress.Address,
|
|
(net_switch_sockaddr_t *) &buf[i].iiNetmask.Address,
|
|
buf[i].iiFlags);
|
|
}
|
|
free(buf);
|
|
}
|
|
#else
|
|
struct ifaddrs *buf;
|
|
if (getifaddrs(&buf) >= 0) {
|
|
for (struct ifaddrs *addr = buf; addr; addr = addr->ifa_next) {
|
|
added += net_switch_add_hostaddr(netswitch,
|
|
(net_switch_sockaddr_t *) addr->ifa_addr,
|
|
(net_switch_sockaddr_t *) ((addr->ifa_flags & IFF_POINTOPOINT) ? addr->ifa_dstaddr : addr->ifa_broadaddr),
|
|
(net_switch_sockaddr_t *) addr->ifa_netmask,
|
|
addr->ifa_flags);
|
|
}
|
|
freeifaddrs(buf);
|
|
}
|
|
#endif
|
|
|
|
/* Add loopback if it's not present. */
|
|
struct sockaddr_in fallback = {
|
|
.sin_family = AF_INET,
|
|
.sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }
|
|
};
|
|
struct sockaddr_in fallback_broadcast = {
|
|
.sin_family = AF_INET,
|
|
.sin_addr = { .s_addr = htonl(INADDR_BROADCAST) }
|
|
};
|
|
added += net_switch_add_hostaddr(netswitch,
|
|
(net_switch_sockaddr_t *) (struct sockaddr *) &fallback,
|
|
(net_switch_sockaddr_t *) (struct sockaddr *) &fallback_broadcast,
|
|
NULL, IFF_UP | IFF_LOOPBACK);
|
|
|
|
/* If no non-loopback interfaces have been successfully added,
|
|
fall back to IPv4 multicast on a single OS-selected interface. */
|
|
if (!added) {
|
|
fallback.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
net_switch_add_hostaddr(netswitch,
|
|
(net_switch_sockaddr_t *) (struct sockaddr *) &fallback,
|
|
(net_switch_sockaddr_t *) (struct sockaddr *) &fallback_broadcast,
|
|
NULL, IFF_UP | IFF_MULTICAST);
|
|
}
|
|
}
|
|
|
|
static void
|
|
net_switch_thread(void *priv)
|
|
{
|
|
net_switch_t *netswitch = (net_switch_t *) priv;
|
|
|
|
/* Start polling. */
|
|
netswitch_log("Network Switch: polling started\n");
|
|
|
|
#ifdef _WIN32
|
|
WSAEventSelect(netswitch->socket_rx, netswitch->sock_event, FD_READ);
|
|
|
|
HANDLE events[NET_EVENT_MAX];
|
|
events[NET_EVENT_STOP] = net_event_get_handle(&netswitch->stop_event);
|
|
events[NET_EVENT_TX] = net_event_get_handle(&netswitch->tx_event);
|
|
events[NET_EVENT_RX] = netswitch->sock_event;
|
|
#else
|
|
struct pollfd pfd[NET_EVENT_MAX];
|
|
pfd[NET_EVENT_STOP].fd = net_event_get_fd(&netswitch->stop_event);
|
|
pfd[NET_EVENT_STOP].events = POLLIN | POLLPRI;
|
|
|
|
pfd[NET_EVENT_TX].fd = net_event_get_fd(&netswitch->tx_event);
|
|
pfd[NET_EVENT_TX].events = POLLIN | POLLPRI;
|
|
|
|
pfd[NET_EVENT_RX].fd = netswitch->socket_rx;
|
|
pfd[NET_EVENT_RX].events = POLLIN | POLLPRI;
|
|
#endif
|
|
|
|
int packets;
|
|
ssize_t len;
|
|
#ifdef _WIN32
|
|
uint8_t run = 1;
|
|
while (run) {
|
|
int ret = WaitForMultipleObjects(NET_EVENT_MAX, events, FALSE, INFINITE);
|
|
switch (ret - WAIT_OBJECT_0) {
|
|
case NET_EVENT_STOP:
|
|
run = 0;
|
|
#else
|
|
while (1) {
|
|
poll(pfd, NET_EVENT_MAX, -1);
|
|
if (pfd[NET_EVENT_STOP].revents & POLLIN) {
|
|
#endif
|
|
net_event_clear(&netswitch->stop_event);
|
|
break;
|
|
#ifdef _WIN32
|
|
|
|
case NET_EVENT_TX:
|
|
#else
|
|
}
|
|
if (pfd[NET_EVENT_TX].revents & POLLIN) {
|
|
#endif
|
|
net_event_clear(&netswitch->tx_event);
|
|
netswitch->during_tx = 1;
|
|
packets = network_tx_popv(netswitch->card, netswitch->pkt_tx_v, SWITCH_PKT_BATCH);
|
|
for (int i = 0; i < packets; i++) {
|
|
#define MAC_FORMAT "(%02X:%02X:%02X:%02X:%02X:%02X -> %02X:%02X:%02X:%02X:%02X:%02X)"
|
|
#define MAC_FORMAT_ARGS(p) (p)[6], (p)[7], (p)[8], (p)[9], (p)[10], (p)[11], (p)[0], (p)[1], (p)[2], (p)[3], (p)[4], (p)[5]
|
|
netswitch_log("Network Switch: sending %d-byte packet " MAC_FORMAT "\n",
|
|
netswitch->pkt_tx_v[i].len, MAC_FORMAT_ARGS(netswitch->pkt_tx_v[i].data));
|
|
|
|
/* Send through all known host interfaces. */
|
|
for (net_switch_hostaddr_t *hostaddr = netswitch->hostaddrs; hostaddr; hostaddr = hostaddr->next)
|
|
sendto(hostaddr->socket_tx,
|
|
(char *) netswitch->pkt_tx_v[i].data, netswitch->pkt_tx_v[i].len, 0,
|
|
&hostaddr->addr_tx.sa, sizeof(hostaddr->addr_tx.sa));
|
|
}
|
|
netswitch->during_tx = 0;
|
|
|
|
if (netswitch->recv_on_tx) {
|
|
do {
|
|
packets = network_rx_on_tx_popv(netswitch->card, netswitch->pkt_tx_v, SWITCH_PKT_BATCH);
|
|
for (int i = 0; i < packets; i++)
|
|
network_rx_put_pkt(netswitch->card, &(netswitch->pkt_tx_v[i]));
|
|
} while (packets > 0);
|
|
netswitch->recv_on_tx = 0;
|
|
}
|
|
#ifdef _WIN32
|
|
break;
|
|
|
|
case NET_EVENT_RX:
|
|
#else
|
|
}
|
|
if (pfd[NET_EVENT_RX].revents & POLLIN) {
|
|
#endif
|
|
len = recv(netswitch->socket_rx, (char *) netswitch->pkt.data, NET_MAX_FRAME, 0);
|
|
if (len < 12) {
|
|
netswitch_log("Network Switch: recv error (%d)\n", len);
|
|
} else if ((AS_U64(netswitch->pkt.data[6]) & le64_to_cpu(0xffffffffffffULL)) == netswitch->mac_addr_u64) {
|
|
/* A packet we've sent has looped back, drop it. */
|
|
} else if (netswitch->promisc || /* promiscuous mode? */
|
|
(netswitch->pkt.data[0] & 1) || /* broadcast packet? */
|
|
((AS_U64(netswitch->pkt.data[0]) & le64_to_cpu(0xffffffffffffULL)) == netswitch->mac_addr_u64)) { /* packet for me? */
|
|
netswitch_log("Network Switch: receiving %d-byte packet " MAC_FORMAT "\n",
|
|
len, MAC_FORMAT_ARGS(netswitch->pkt.data));
|
|
netswitch->pkt.len = len;
|
|
if (netswitch->during_tx) {
|
|
network_rx_on_tx_put_pkt(netswitch->card, &netswitch->pkt);
|
|
netswitch->recv_on_tx = 1;
|
|
} else {
|
|
network_rx_put_pkt(netswitch->card, &netswitch->pkt);
|
|
}
|
|
} else {
|
|
netswitch_log("Network Switch: dropping %d-byte packet " MAC_FORMAT "\n",
|
|
len, MAC_FORMAT_ARGS(netswitch->pkt.data));
|
|
}
|
|
#ifdef _WIN32
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
netswitch_log("Network Switch: polling stopped\n");
|
|
}
|
|
|
|
static void net_switch_close(void *priv);
|
|
|
|
void *
|
|
net_switch_init(const netcard_t *card, const uint8_t *mac_addr, void *priv, char *netdrv_errbuf)
|
|
{
|
|
netcard_conf_t *netcard = (netcard_conf_t *) priv;
|
|
|
|
netswitch_log("Network Switch: initializing with group %d...\n", netcard->switch_group);
|
|
|
|
net_switch_t *netswitch = calloc(1, sizeof(net_switch_t));
|
|
memcpy(netswitch->mac_addr, mac_addr, sizeof(netswitch->mac_addr));
|
|
netswitch->card = (netcard_t *) card;
|
|
netswitch->promisc = !!netcard->promisc_mode;
|
|
|
|
/* Initialize receive socket. */
|
|
netswitch->socket_rx = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (netswitch->socket_rx < 0) {
|
|
strncpy(netdrv_errbuf, "Could not initialize receive socket\n", NET_DRV_ERRBUF_SIZE);
|
|
goto fail;
|
|
}
|
|
|
|
int val = 1;
|
|
if (setsockopt(netswitch->socket_rx, SOL_SOCKET, SO_REUSEADDR, (char *) &val, sizeof(val)) < 0) {
|
|
strncpy(netdrv_errbuf, "Could not set SO_REUSEADDR\n", NET_DRV_ERRBUF_SIZE);
|
|
goto fail;
|
|
}
|
|
#ifndef _WIN32
|
|
if (setsockopt(netswitch->socket_rx, SOL_SOCKET, SO_REUSEPORT, (char *) &val, sizeof(val)) < 0) {
|
|
strncpy(netdrv_errbuf, "Could not set SO_REUSEPORT\n", NET_DRV_ERRBUF_SIZE);
|
|
goto fail;
|
|
}
|
|
#endif
|
|
|
|
/* Disable multicast loopback on Windows. (no harm on other platforms) */
|
|
val = 0;
|
|
setsockopt(netswitch->socket_rx, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &val, sizeof(val));
|
|
|
|
netswitch->port_out = htons(SWITCH_MULTICAST_PORT - NET_SWITCH_GRP_MIN + netcard->switch_group);
|
|
struct sockaddr_in addr = {
|
|
.sin_family = AF_INET,
|
|
.sin_addr = { .s_addr = htonl(INADDR_ANY) },
|
|
.sin_port = netswitch->port_out
|
|
};
|
|
if (bind(netswitch->socket_rx, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
|
snprintf(netdrv_errbuf, NET_DRV_ERRBUF_SIZE, "Could not bind to port %d\n", (int) addr.sin_port);
|
|
goto fail;
|
|
}
|
|
|
|
/* Add host interfaces. */
|
|
net_switch_update_hostaddrs(netswitch);
|
|
if (!netswitch->hostaddrs) {
|
|
strncpy(netdrv_errbuf, "Could not add any interfaces\n", NET_DRV_ERRBUF_SIZE);
|
|
goto fail;
|
|
}
|
|
|
|
for (int i = 0; i < SWITCH_PKT_BATCH; i++)
|
|
netswitch->pkt_tx_v[i].data = calloc(1, NET_MAX_FRAME);
|
|
netswitch->pkt.data = calloc(1, NET_MAX_FRAME);
|
|
net_event_init(&netswitch->tx_event);
|
|
net_event_init(&netswitch->stop_event);
|
|
#ifdef _WIN32
|
|
netswitch->sock_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
#endif
|
|
|
|
netswitch_log("Network Switch: creating thread...\n");
|
|
netswitch->poll_tid = thread_create(net_switch_thread, netswitch);
|
|
|
|
return netswitch;
|
|
|
|
fail:
|
|
net_switch_close(netswitch);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
net_switch_close(void *priv)
|
|
{
|
|
if (!priv)
|
|
return;
|
|
|
|
net_switch_t *netswitch = (net_switch_t *) priv;
|
|
|
|
netswitch_log("Network Switch: closing\n");
|
|
|
|
if (netswitch->poll_tid) {
|
|
/* Tell the polling thread to shut down. */
|
|
net_event_set(&netswitch->stop_event);
|
|
|
|
/* Wait for the thread to finish. */
|
|
netswitch_log("Network Switch: waiting for thread to end...\n");
|
|
thread_wait(netswitch->poll_tid);
|
|
}
|
|
|
|
net_switch_hostaddr_t *hostaddr = netswitch->hostaddrs;
|
|
while (hostaddr) {
|
|
if (hostaddr->socket_tx >= 0)
|
|
close(hostaddr->socket_tx);
|
|
net_switch_hostaddr_t *next = hostaddr->next;
|
|
free(hostaddr);
|
|
hostaddr = next;
|
|
}
|
|
if (netswitch->socket_rx >= 0)
|
|
close(netswitch->socket_rx);
|
|
net_event_close(&netswitch->stop_event);
|
|
net_event_close(&netswitch->tx_event);
|
|
for (int i = 0; i < SWITCH_PKT_BATCH; i++)
|
|
free(netswitch->pkt_tx_v[i].data);
|
|
free(netswitch->pkt.data);
|
|
free(netswitch);
|
|
}
|
|
|
|
const netdrv_t net_switch_drv = {
|
|
.notify_in = &net_switch_in_available,
|
|
.init = &net_switch_init,
|
|
.close = &net_switch_close,
|
|
.priv = NULL
|
|
};
|