/* sim_slirp.c: | |
------------------------------------------------------------------------------ | |
Copyright (c) 2015, Mark Pizzolato | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of the author shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from the author. | |
------------------------------------------------------------------------------ | |
This module provides the interface needed between sim_ether and SLiRP to | |
provide NAT network functionality. | |
*/ | |
/* Actual slirp API interface support, some code taken from slirpvde.c */ | |
#define DEFAULT_IP_ADDR "10.0.2.2" | |
#include "glib.h" | |
#include "qemu/timer.h" | |
#include "libslirp.h" | |
#include "sim_defs.h" | |
#include "sim_slirp.h" | |
#include "sim_sock.h" | |
#include "libslirp.h" | |
#define IS_TCP 0 | |
#define IS_UDP 1 | |
static const char *tcpudp[] = { | |
"TCP", | |
"UDP" | |
}; | |
struct redir_tcp_udp { | |
struct in_addr inaddr; | |
int is_udp; | |
int port; | |
int lport; | |
struct redir_tcp_udp *next; | |
}; | |
static int | |
_parse_redirect_port (struct redir_tcp_udp **head, char *buff, int is_udp) | |
{ | |
uint32 inaddr = 0; | |
int port = 0; | |
int lport = 0; | |
char *ipaddrstr = NULL; | |
char *portstr = NULL; | |
struct redir_tcp_udp *new; | |
if (((ipaddrstr = strchr(buff, ':')) == NULL) || (*(ipaddrstr+1) == 0)) { | |
sim_printf ("redir %s syntax error\n", tcpudp[is_udp]); | |
return -1; | |
} | |
*ipaddrstr++ = 0; | |
if (((portstr = strchr (ipaddrstr, ':')) == NULL) || (*(portstr+1) == 0)) { | |
sim_printf ("redir %s syntax error\n", tcpudp[is_udp]); | |
return -1; | |
} | |
*portstr++ = 0; | |
sscanf (buff, "%d", &lport); | |
sscanf (portstr, "%d", &port); | |
if (ipaddrstr) | |
inaddr = inet_addr (ipaddrstr); | |
if (!inaddr) { | |
sim_printf ("%s redirection error: an IP address must be specified\n", tcpudp[is_udp]); | |
return -1; | |
} | |
if ((new = g_malloc (sizeof(struct redir_tcp_udp))) == NULL) | |
return -1; | |
else { | |
inet_aton (ipaddrstr, &new->inaddr); | |
new->is_udp = is_udp; | |
new->port = port; | |
new->lport = lport; | |
new->next = *head; | |
*head = new; | |
return 0; | |
} | |
} | |
static int _do_redirects (Slirp *slirp, struct redir_tcp_udp *head) | |
{ | |
struct in_addr host_addr; | |
int ret = 0; | |
host_addr.s_addr = htonl(INADDR_ANY); | |
if (head) { | |
ret = _do_redirects (slirp, head->next); | |
if (slirp_add_hostfwd (slirp, head->is_udp, host_addr, head->lport, head->inaddr, head->port) < 0) { | |
sim_printf("Can't establish redirector for: redir %s =%d:%s:%d\n", | |
tcpudp[head->is_udp], head->lport, inet_ntoa(head->inaddr), head->port); | |
++ret; | |
} | |
} | |
return ret; | |
} | |
struct sim_slirp { | |
Slirp *slirp; | |
char *args; | |
struct in_addr vnetwork; | |
struct in_addr vnetmask; | |
int maskbits; | |
struct in_addr vgateway; | |
int dhcpmgmt; | |
struct in_addr vdhcp_start; | |
struct in_addr vnameserver; | |
char *boot_file; | |
char *tftp_path; | |
char *dns_search; | |
char **dns_search_domains; | |
struct redir_tcp_udp *rtcp; | |
GArray *gpollfds; | |
SOCKET db_chime; /* write packet doorbell */ | |
struct write_request { | |
struct write_request *next; | |
char msg[1518]; | |
size_t len; | |
} *write_requests; | |
struct write_request *write_buffers; | |
pthread_mutex_t write_buffer_lock; | |
void *opaque; /* opaque value passed during packet delivery */ | |
packet_callback callback; /* slirp arriving packet delivery callback */ | |
DEVICE *dptr; | |
uint32 dbit; | |
}; | |
DEVICE *slirp_dptr; | |
uint32 slirp_dbit; | |
SLIRP *sim_slirp_open (const char *args, void *opaque, packet_callback callback, DEVICE *dptr, uint32 dbit) | |
{ | |
SLIRP *slirp = (SLIRP *)g_malloc0(sizeof(*slirp)); | |
char *targs = g_strdup (args); | |
char *tptr = targs; | |
char *cptr; | |
char tbuf[CBUFSIZE], gbuf[CBUFSIZE]; | |
int err; | |
slirp_dptr = dptr; | |
slirp_dbit = dbit; | |
slirp->args = (char *)g_malloc0(1 + strlen(args)); | |
strcpy (slirp->args, args); | |
slirp->opaque = opaque; | |
slirp->callback = callback; | |
slirp->maskbits = 24; | |
slirp->dhcpmgmt = 1; | |
slirp->db_chime = INVALID_SOCKET; | |
inet_aton(DEFAULT_IP_ADDR,&slirp->vgateway); | |
err = 0; | |
while (*tptr && !err) { | |
tptr = get_glyph_nc (tptr, tbuf, ','); | |
if (!tbuf[0]) | |
break; | |
cptr = tbuf; | |
cptr = get_glyph (cptr, gbuf, '='); | |
if (0 == MATCH_CMD (gbuf, "DHCP")) { | |
slirp->dhcpmgmt = 1; | |
if (cptr && *cptr) | |
inet_aton (cptr, &slirp->vdhcp_start); | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "TFTP")) { | |
if (cptr && *cptr) | |
slirp->tftp_path = g_strdup (cptr); | |
else { | |
sim_printf ("Missing TFTP Path\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "BOOTFILE")) { | |
if (cptr && *cptr) | |
slirp->boot_file = g_strdup (cptr); | |
else { | |
sim_printf ("Missing DHCP Boot file name\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if ((0 == MATCH_CMD (gbuf, "NAMESERVER")) || | |
(0 == MATCH_CMD (gbuf, "DNS"))) { | |
if (cptr && *cptr) | |
inet_aton (cptr, &slirp->vnameserver); | |
else { | |
sim_printf ("Missing nameserver\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "DNSSEARCH")) { | |
if (cptr && *cptr) { | |
int count = 0; | |
char *name; | |
slirp->dns_search = g_strdup (cptr); | |
name = slirp->dns_search; | |
do { | |
++count; | |
slirp->dns_search_domains = realloc (slirp->dns_search_domains, (count + 1)*sizeof(char *)); | |
slirp->dns_search_domains[count] = NULL; | |
slirp->dns_search_domains[count-1] = name; | |
name = strchr (name, ':'); | |
if (name) { | |
*name = '\0'; | |
++name; | |
} | |
} while (name && *name); | |
} | |
else { | |
sim_printf ("Missing DNS search list\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "GATEWAY")) { | |
if (cptr && *cptr) { | |
char *slash = strchr (cptr, '/'); | |
if (slash) { | |
slirp->maskbits = atoi (slash+1); | |
*slash = '\0'; | |
} | |
inet_aton (cptr, &slirp->vgateway); | |
} | |
else { | |
sim_printf ("Missing host\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "NETWORK")) { | |
if (cptr && *cptr) { | |
char *slash = strchr (cptr, '/'); | |
if (slash) { | |
slirp->maskbits = atoi (slash+1); | |
*slash = '\0'; | |
} | |
inet_aton (cptr, &slirp->vnetwork); | |
} | |
else { | |
sim_printf ("Missing network\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "NODHCP")) { | |
slirp->dhcpmgmt = 0; | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "UDP")) { | |
if (cptr && *cptr) | |
err = _parse_redirect_port (&slirp->rtcp, cptr, IS_UDP); | |
else { | |
sim_printf ("Missing UDP port mapping\n"); | |
err = 1; | |
} | |
continue; | |
} | |
if (0 == MATCH_CMD (gbuf, "TCP")) { | |
if (cptr && *cptr) | |
err = _parse_redirect_port (&slirp->rtcp, cptr, IS_TCP); | |
else { | |
sim_printf ("Missing TCP port mapping\n"); | |
err = 1; | |
} | |
continue; | |
} | |
sim_printf ("Unexpected NAT argument: %s\n", gbuf); | |
err = 1; | |
} | |
if (err) { | |
sim_slirp_close (slirp); | |
g_free (targs); | |
return NULL; | |
} | |
slirp->vnetmask.s_addr = htonl(~((1 << (32-slirp->maskbits)) - 1)); | |
slirp->vnetwork.s_addr = slirp->vgateway.s_addr & slirp->vnetmask.s_addr; | |
if ((slirp->vgateway.s_addr & ~slirp->vnetmask.s_addr) == 0) | |
slirp->vgateway.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 2); | |
if ((slirp->vdhcp_start.s_addr == 0) && slirp->dhcpmgmt) | |
slirp->vdhcp_start.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 15); | |
if (slirp->vnameserver.s_addr == 0) | |
slirp->vnameserver.s_addr = htonl(ntohl(slirp->vnetwork.s_addr) | 3); | |
slirp->slirp = slirp_init (0, slirp->vnetwork, slirp->vnetmask, slirp->vgateway, | |
NULL, slirp->tftp_path, slirp->boot_file, | |
slirp->vdhcp_start, slirp->vnameserver, | |
(const char **)(slirp->dns_search_domains), (void *)slirp); | |
if (_do_redirects (slirp->slirp, slirp->rtcp)) { | |
sim_slirp_close (slirp); | |
slirp = NULL; | |
} | |
else { | |
char db_host[32]; | |
GPollFD pfd; | |
int64_t rnd_val = qemu_clock_get_ns (0) / 1000000; | |
pthread_mutex_init (&slirp->write_buffer_lock, NULL); | |
slirp->gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); | |
/* setup transmit packet wakeup doorbell */ | |
do { | |
if ((rnd_val & 0xFFFF) == 0) | |
++rnd_val; | |
sprintf (db_host, "localhost:%d", (int)(rnd_val & 0xFFFF)); | |
slirp->db_chime = sim_connect_sock_ex (db_host, db_host, NULL, NULL, SIM_SOCK_OPT_DATAGRAM); | |
} while (slirp->db_chime == INVALID_SOCKET); | |
memset (&pfd, 0, sizeof (pfd)); | |
pfd.fd = slirp->db_chime; | |
pfd.events = G_IO_IN; | |
g_array_append_val(slirp->gpollfds, pfd); | |
slirp->dbit = dbit; | |
slirp->dptr = dptr; | |
sim_slirp_show(slirp, stdout); | |
if (sim_log && (sim_log != stdout)) | |
sim_slirp_show(slirp, sim_log); | |
if (sim_deb && (sim_deb != stdout) && (sim_deb != sim_log)) | |
sim_slirp_show(slirp, sim_deb); | |
} | |
g_free (targs); | |
return slirp; | |
} | |
void sim_slirp_close (SLIRP *slirp) | |
{ | |
struct redir_tcp_udp *rtmp; | |
if (slirp) { | |
g_free (slirp->args); | |
g_free (slirp->tftp_path); | |
g_free (slirp->boot_file); | |
g_free (slirp->dns_search); | |
g_free (slirp->dns_search_domains); | |
while ((rtmp = slirp->rtcp)) { | |
slirp_remove_hostfwd(slirp->slirp, rtmp->is_udp, rtmp->inaddr, rtmp->lport); | |
slirp->rtcp = rtmp->next; | |
g_free (rtmp); | |
} | |
g_array_free(slirp->gpollfds, true); | |
if (slirp->db_chime != INVALID_SOCKET) | |
closesocket (slirp->db_chime); | |
if (1) { | |
struct write_request *buffer; | |
while (NULL != (buffer = slirp->write_buffers)) { | |
slirp->write_buffers = buffer->next; | |
free(buffer); | |
} | |
while (NULL != (buffer = slirp->write_requests)) { | |
slirp->write_requests = buffer->next; | |
free(buffer); | |
} | |
} | |
pthread_mutex_destroy (&slirp->write_buffer_lock); | |
if (slirp->slirp) | |
slirp_cleanup(slirp->slirp); | |
} | |
g_free (slirp); | |
} | |
t_stat sim_slirp_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "%s", | |
"NAT options:\n" | |
" DHCP{=dhcp_start_address} Enables DHCP server and specifies\n" | |
" guest LAN DHCP start IP address\n" | |
" BOOTFILE=bootfilename specifies DHCP returned Boot Filename\n" | |
" TFTP=tftp-base-path Enables TFTP server and specifies\n" | |
" base file path\n" | |
" NAMESERVER=nameserver_ipaddres specifies DHCP nameserver IP address\n" | |
" DNS=nameserver_ipaddres specifies DHCP nameserver IP address\n" | |
" DNSSEARCH=domain{:domain{:domain}} specifies DNS Domains search suffixes\n" | |
" GATEWAY=host_ipaddress{/masklen} specifies LAN gateway IP address\n" | |
" NETWORK=network_ipaddress{/masklen} specifies LAN network address\n" | |
" UDP=port:address:internal-port maps host UDP port to guest port\n" | |
" TCP=port:address:internal-port maps host TCP port to guest port\n" | |
" NODHCP disables DHCP server\n" | |
"Default NAT Options: GATEWAY=10.0.2.2, masklen=24(netmask is 255.255.255.0)\n" | |
" DHCP=10.0.2.15, NAMESERVER=10.0.2.3\n" | |
" Nameserver defaults to proxy traffic to host system's active nameserver\n\n" | |
"NAT limitations\n\n" | |
"There are four limitations of NAT mode which users should be aware of:\n\n" | |
" 1) ICMP protocol limitations:\n" | |
" Some frequently used network debugging tools (e.g. ping or tracerouting)\n" | |
" rely on the ICMP protocol for sending/receiving messages. While some\n" | |
" ICMP support is available on some hosts (ping may or may not work),\n" | |
" some other tools may not work reliably.\n\n" | |
" 2) Receiving of UDP broadcasts is not reliable:\n" | |
" The guest does not reliably receive broadcasts, since, in order to save\n" | |
" resources, it only listens for a certain amount of time after the guest\n" | |
" has sent UDP data on a particular port.\n\n" | |
" 3) Protocols such as GRE, DECnet, LAT and Clustering are unsupported:\n" | |
" Protocols other than TCP and UDP are not supported.\n\n" | |
" 4) Forwarding host ports < 1024 impossible:\n" | |
" On Unix-based hosts (e.g. Linux, Solaris, Mac OS X) it is not possible\n" | |
" to bind to ports below 1024 from applications that are not run by root.\n" | |
" As a result, if you try to configure such a port forwarding, the attach\n" | |
" will fail.\n\n" | |
"These limitations normally don't affect standard network use. But the\n" | |
"presence of NAT has also subtle effects that may interfere with protocols\n" | |
"that are normally working. One example is NFS, where the server is often\n" | |
"configured to refuse connections from non-privileged ports (i.e. ports not\n" | |
" below 1024).\n" | |
); | |
return SCPE_OK; | |
} | |
int sim_slirp_send (SLIRP *slirp, const char *msg, size_t len, int flags) | |
{ | |
struct write_request *request; | |
int wake_needed = 0; | |
/* Get a buffer */ | |
pthread_mutex_lock (&slirp->write_buffer_lock); | |
if (NULL != (request = slirp->write_buffers)) | |
slirp->write_buffers = request->next; | |
pthread_mutex_unlock (&slirp->write_buffer_lock); | |
if (NULL == request) | |
request = (struct write_request *)g_malloc(sizeof(*request)); | |
/* Copy buffer contents */ | |
request->len = len; | |
memcpy(request->msg, msg, len); | |
/* Insert buffer at the end of the write list (to make sure that */ | |
/* packets make it to the wire in the order they were presented here) */ | |
pthread_mutex_lock (&slirp->write_buffer_lock); | |
request->next = NULL; | |
if (slirp->write_requests) { | |
struct write_request *last_request = slirp->write_requests; | |
while (last_request->next) { | |
last_request = last_request->next; | |
} | |
last_request->next = request; | |
} | |
else { | |
slirp->write_requests = request; | |
wake_needed = 1; | |
} | |
pthread_mutex_unlock (&slirp->write_buffer_lock); | |
if (wake_needed) | |
sim_write_sock (slirp->db_chime, msg, 0); | |
return len; | |
} | |
void slirp_output (void *opaque, const uint8_t *pkt, int pkt_len) | |
{ | |
SLIRP *slirp = (SLIRP *)opaque; | |
slirp->callback (slirp->opaque, pkt, pkt_len); | |
} | |
void sim_slirp_show (SLIRP *slirp, FILE *st) | |
{ | |
struct redir_tcp_udp *rtmp; | |
if ((slirp == NULL) || (slirp->slirp == NULL)) | |
return; | |
fprintf (st, "NAT args: %s\n", slirp->args); | |
fprintf (st, "NAT network setup:\n"); | |
fprintf (st, " gateway =%s/%d", inet_ntoa(slirp->vgateway), slirp->maskbits); | |
fprintf (st, "(%s)\n", inet_ntoa(slirp->vnetmask)); | |
fprintf (st, " DNS =%s\n", inet_ntoa(slirp->vnameserver)); | |
if (slirp->vdhcp_start.s_addr != 0) | |
fprintf (st, " dhcp_start =%s\n", inet_ntoa(slirp->vdhcp_start)); | |
if (slirp->boot_file) | |
fprintf (st, " dhcp bootfile =%s\n", slirp->boot_file); | |
if (slirp->dns_search_domains) { | |
char **domains = slirp->dns_search_domains; | |
fprintf (st, " DNS domains ="); | |
while (*domains) { | |
fprintf (st, "%s%s", (domains != slirp->dns_search_domains) ? ", " : "", *domains); | |
++domains; | |
} | |
fprintf (st, "\n"); | |
} | |
if (slirp->tftp_path) | |
fprintf (st, " tftp prefix =%s\n", slirp->tftp_path); | |
rtmp = slirp->rtcp; | |
while (rtmp) { | |
fprintf (st, " redir %3s =%d:%s:%d\n", tcpudp[rtmp->is_udp], rtmp->lport, inet_ntoa(rtmp->inaddr), rtmp->port); | |
rtmp = rtmp->next; | |
} | |
slirp_connection_info (slirp->slirp, (Monitor *)st); | |
} | |
#if !defined(MAX) | |
#define MAX(a,b) (((a)>(b)) ? (a) : (b)) | |
#endif | |
static int pollfds_fill (GArray *pollfds, fd_set *rfds, fd_set *wfds, | |
fd_set *xfds) | |
{ | |
int nfds = -1; | |
guint i; | |
for (i = 0; i < pollfds->len; i++) { | |
GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); | |
int fd = pfd->fd; | |
int events = pfd->events; | |
if (events & G_IO_IN) { | |
FD_SET(fd, rfds); | |
nfds = MAX(nfds, fd); | |
} | |
if (events & G_IO_OUT) { | |
FD_SET(fd, wfds); | |
nfds = MAX(nfds, fd); | |
} | |
if (events & (G_IO_PRI | G_IO_HUP | G_IO_ERR)) { | |
FD_SET(fd, xfds); | |
nfds = MAX(nfds, fd); | |
} | |
} | |
return nfds; | |
} | |
static void pollfds_poll (GArray *pollfds, int nfds, fd_set *rfds, | |
fd_set *wfds, fd_set *xfds) | |
{ | |
guint i; | |
for (i = 0; i < pollfds->len; i++) { | |
GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); | |
int fd = pfd->fd; | |
int revents = 0; | |
if (FD_ISSET(fd, rfds)) { | |
revents |= G_IO_IN; | |
} | |
if (FD_ISSET(fd, wfds)) { | |
revents |= G_IO_OUT; | |
} | |
if (FD_ISSET(fd, xfds)) { | |
revents |= G_IO_PRI; | |
} | |
pfd->revents = revents & pfd->events; | |
} | |
} | |
int sim_slirp_select (SLIRP *slirp, int ms_timeout) | |
{ | |
int select_ret = 0; | |
uint32 slirp_timeout = ms_timeout; | |
struct timeval timeout; | |
fd_set rfds, wfds, xfds; | |
fd_set save_rfds, save_wfds, save_xfds; | |
int nfds; | |
/* Populate the GPollFDs from slirp */ | |
g_array_set_size (slirp->gpollfds, 1); /* Leave the doorbell chime alone */ | |
slirp_pollfds_fill(slirp->gpollfds, &slirp_timeout); | |
timeout.tv_sec = slirp_timeout / 1000; | |
timeout.tv_usec = (slirp_timeout % 1000) * 1000; | |
FD_ZERO(&rfds); | |
FD_ZERO(&wfds); | |
FD_ZERO(&xfds); | |
/* Extract the GPollFDs interest */ | |
nfds = pollfds_fill (slirp->gpollfds, &rfds, &wfds, &xfds); | |
save_rfds = rfds; | |
save_wfds = wfds; | |
save_xfds = xfds; | |
select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &timeout); | |
if (select_ret) { | |
int i; | |
/* Update the GPollFDs results */ | |
pollfds_poll (slirp->gpollfds, nfds, &rfds, &wfds, &xfds); | |
if (FD_ISSET (slirp->db_chime, &rfds)) { | |
char buf[32]; | |
/* consume the doorbell wakeup ring */ | |
recv (slirp->db_chime, buf, sizeof (buf), 0); | |
} | |
sim_debug (slirp->dbit, slirp->dptr, "Select returned %d\r\n", select_ret); | |
for (i=0; i<nfds+1; i++) { | |
if (FD_ISSET(i, &rfds) || FD_ISSET(i, &save_rfds)) | |
sim_debug (slirp->dbit, slirp->dptr, "%d: save_rfd=%d, rfd=%d\r\n", i, FD_ISSET(i, &save_rfds), FD_ISSET(i, &rfds)); | |
if (FD_ISSET(i, &wfds) || FD_ISSET(i, &save_wfds)) | |
sim_debug (slirp->dbit, slirp->dptr, "%d: save_wfd=%d, wfd=%d\r\n", i, FD_ISSET(i, &save_wfds), FD_ISSET(i, &wfds)); | |
if (FD_ISSET(i, &xfds) || FD_ISSET(i, &save_xfds)) | |
sim_debug (slirp->dbit, slirp->dptr, "%d: save_xfd=%d, xfd=%d\r\n", i, FD_ISSET(i, &save_xfds), FD_ISSET(i, &xfds)); | |
} | |
} | |
return select_ret + 1; /* Force dispatch even on timeout */ | |
} | |
void sim_slirp_dispatch (SLIRP *slirp) | |
{ | |
struct write_request *request; | |
/* first deliver any transmit packets which are pending */ | |
pthread_mutex_lock (&slirp->write_buffer_lock); | |
while (NULL != (request = slirp->write_requests)) { | |
/* Pull buffer off request list */ | |
slirp->write_requests = request->next; | |
pthread_mutex_unlock (&slirp->write_buffer_lock); | |
slirp_input (slirp->slirp, (const uint8_t *)request->msg, (int)request->len); | |
pthread_mutex_lock (&slirp->write_buffer_lock); | |
/* Put buffer on free buffer list */ | |
request->next = slirp->write_buffers; | |
slirp->write_buffers = request; | |
} | |
pthread_mutex_unlock (&slirp->write_buffer_lock); | |
slirp_pollfds_poll(slirp->gpollfds, 0); | |
} | |