Slirp: reduce differences to latest Qemu sources

This commit is contained in:
Volker Ruppert 2014-02-18 20:34:14 +00:00
parent 0a1b4f1c7e
commit 6b39b6971f
10 changed files with 442 additions and 383 deletions

View File

@ -137,10 +137,9 @@ void bx_slirp_new_pktmover_c::rx_timer_handler(void *this_ptr)
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&xfds);
slirp_update_timeout(&timeout);
slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
slirp_select_fill(&nfds, &rfds, &wfds, &xfds, &timeout);
tv.tv_sec = 0;
tv.tv_usec = timeout;
tv.tv_usec = 0;
ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0));
}

View File

@ -25,6 +25,10 @@ typedef Bit64s ssize_t;
#define strcasecmp _stricmp
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type *) 0)->member) *__mptr = (ptr); \

View File

@ -15,9 +15,8 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnameserver, void *opaque);
void slirp_cleanup(Slirp *slirp);
void slirp_update_timeout(uint32_t *timeout);
void slirp_select_fill(int *pnfds,
fd_set *readfds, fd_set *writefds, fd_set *xfds);
void slirp_select_fill(int *pnfds, fd_set *readfds, fd_set *writefds,
fd_set *xfds, uint32_t *timeout);
void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
int select_error);

View File

@ -33,6 +33,7 @@ extern char *exec_shell;
extern Bit64u curtime;
extern fd_set *global_readfds, *global_writefds, *global_xfds;
extern struct in_addr loopback_addr;
extern unsigned long loopback_mask;
extern char *username;
extern char *socket_path;
extern int towrite_max;

View File

@ -49,22 +49,6 @@
* free the m_ext. This is inefficient memory-wise, but who cares.
*/
/* XXX should union some of these! */
/* header at beginning of each mbuf: */
struct m_hdr {
struct mbuf *mh_next; /* Linked list of mbufs */
struct mbuf *mh_prev;
struct mbuf *mh_nextpkt; /* Next packet in queue/record */
struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
int mh_flags; /* Misc flags */
int mh_size; /* Size of data */
struct socket *mh_so;
caddr_t mh_data; /* Location of data */
int mh_len; /* Amount of data in this mbuf */
};
/*
* How much room is in the mbuf, from m_data to the end of the mbuf
*/
@ -80,29 +64,30 @@ struct m_hdr {
#define M_TRAILINGSPACE M_FREEROOM
struct mbuf {
struct m_hdr m_hdr;
/* XXX should union some of these! */
/* header at beginning of each mbuf: */
struct mbuf *m_next; /* Linked list of mbufs */
struct mbuf *m_prev;
struct mbuf *m_nextpkt; /* Next packet in queue/record */
struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */
int m_flags; /* Misc flags */
int m_size; /* Size of data */
struct socket *m_so;
caddr_t m_data; /* Location of data */
int m_len; /* Amount of data in this mbuf */
Slirp *slirp;
bool arp_requested;
uint64_t expiration_date;
/* start of dynamic buffer area, must be last element */
union M_dat {
char m_dat_[1]; /* ANSI don't like 0 sized arrays */
char *m_ext_;
} M_dat;
union {
char m_dat[1]; /* ANSI don't like 0 sized arrays */
char *m_ext;
};
};
#define m_next m_hdr.mh_next
#define m_prev m_hdr.mh_prev
#define m_nextpkt m_hdr.mh_nextpkt
#define m_prevpkt m_hdr.mh_prevpkt
#define m_flags m_hdr.mh_flags
#define m_len m_hdr.mh_len
#define m_data m_hdr.mh_data
#define m_size m_hdr.mh_size
#define m_dat M_dat.m_dat_
#define m_ext M_dat.m_ext_
#define m_so m_hdr.mh_so
#define ifq_prev m_prev
#define ifq_next m_next
#define ifs_prev m_prevpkt

View File

@ -28,6 +28,8 @@
/* host loopback address */
struct in_addr loopback_addr;
/* host loopback network mask */
unsigned long loopback_mask;
/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
static const uint8_t special_ethaddr[ETH_ALEN] = {
@ -40,8 +42,6 @@ static const uint8_t zero_ethaddr[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
fd_set *global_readfds, *global_writefds, *global_xfds;
Bit64u curtime;
static Bit64u time_fasttimo, last_slowtimo;
static int do_slowtimo;
static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
QTAILQ_HEAD_INITIALIZER(slirp_instances);
@ -49,6 +49,11 @@ static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances =
static struct in_addr dns_addr;
static u_int dns_addr_time;
#define TIMEOUT_FAST 2 /* milliseconds */
#define TIMEOUT_SLOW 499 /* milliseconds */
/* for the aging of certain requests like DNS */
#define TIMEOUT_DEFAULT 1000 /* milliseconds */
#ifdef WIN32
#include <iphlpapi.h>
@ -61,7 +66,7 @@ int get_dns_addr(struct in_addr *pdns_addr)
IP_ADDR_STRING *pIPAddr;
struct in_addr tmp_addr;
if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
*pdns_addr = dns_addr;
return 0;
}
@ -119,7 +124,7 @@ int get_dns_addr(struct in_addr *pdns_addr)
if (dns_addr.s_addr != 0) {
struct stat old_stat;
if ((curtime - dns_addr_time) < 1000) {
if ((curtime - dns_addr_time) < TIMEOUT_DEFAULT) {
*pdns_addr = dns_addr;
return 0;
}
@ -194,6 +199,7 @@ static void slirp_init_once(void)
#endif
loopback_addr.s_addr = htonl(INADDR_LOOPBACK);
loopback_mask = htonl(IN_CLASSA_NET);
}
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
@ -254,15 +260,34 @@ void slirp_cleanup(Slirp *slirp)
#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
void slirp_update_timeout(uint32_t *timeout)
static void slirp_update_timeout(uint32_t *timeout)
{
if (!QTAILQ_EMPTY(&slirp_instances)) {
*timeout = MIN(1000, *timeout);
Slirp *slirp;
uint32_t t;
if (*timeout <= TIMEOUT_FAST) {
return;
}
t = MIN(1000, *timeout);
/* If we have tcp timeout with slirp, then we will fill @timeout with
* more precise value.
*/
QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
if (slirp->time_fasttimo) {
*timeout = TIMEOUT_FAST;
return;
}
if (slirp->do_slowtimo) {
t = MIN(TIMEOUT_SLOW, t);
}
}
*timeout = t;
}
void slirp_select_fill(int *pnfds,
fd_set *readfds, fd_set *writefds, fd_set *xfds)
void slirp_select_fill(int *pnfds, fd_set *readfds, fd_set *writefds,
fd_set *xfds, uint32_t *timeout)
{
Slirp *slirp;
struct socket *so, *so_next;
@ -278,135 +303,138 @@ void slirp_select_fill(int *pnfds,
global_xfds = NULL;
nfds = *pnfds;
/*
* First, TCP sockets
*/
do_slowtimo = 0;
/*
* First, TCP sockets
*/
QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
/*
* *_slowtimo needs calling if there are IP fragments
* in the fragment queue, or there are TCP connections active
*/
do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
(&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
/*
* *_slowtimo needs calling if there are IP fragments
* in the fragment queue, or there are TCP connections active
*/
slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) ||
(&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
/*
* See if we need a tcp_fasttimo
*/
if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
time_fasttimo = curtime; /* Flag when we want a fasttimo */
/*
* See if we need a tcp_fasttimo
*/
if (slirp->time_fasttimo == 0 &&
so->so_tcpcb->t_flags & TF_DELACK) {
slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */
}
/*
* NOFDREF can include still connecting to local-host,
* newly socreated() sockets etc. Don't want to select these.
*/
if (so->so_state & SS_NOFDREF || so->s == -1)
continue;
/*
* NOFDREF can include still connecting to local-host,
* newly socreated() sockets etc. Don't want to select these.
*/
if (so->so_state & SS_NOFDREF || so->s == -1) {
continue;
}
/*
* Set for reading sockets which are accepting
*/
if (so->so_state & SS_FACCEPTCONN) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for reading sockets which are accepting
*/
if (so->so_state & SS_FACCEPTCONN) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for writing sockets which are connecting
*/
if (so->so_state & SS_ISFCONNECTING) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for writing sockets which are connecting
*/
if (so->so_state & SS_ISFCONNECTING) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for writing if we are connected, can send more, and
* we have something to send
*/
if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
}
/*
* Set for writing if we are connected, can send more, and
* we have something to send
*/
if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
}
/*
* Set for reading (and urgent data) if we are connected, can
* receive more, and we have room for it XXX /2 ?
*/
if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
FD_SET(so->s, readfds);
FD_SET(so->s, xfds);
UPD_NFDS(so->s);
}
}
/*
* Set for reading (and urgent data) if we are connected, can
* receive more, and we have room for it XXX /2 ?
*/
if (CONN_CANFRCV(so) &&
(so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
FD_SET(so->s, readfds);
FD_SET(so->s, xfds);
UPD_NFDS(so->s);
}
}
/*
* UDP sockets
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
/*
* UDP sockets
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
/*
* See if it's timed out
*/
if (so->so_expire) {
if (so->so_expire <= curtime) {
udp_detach(so);
continue;
} else
do_slowtimo = 1; /* Let socket expire */
}
/*
* When UDP packets are received from over the
* link, they're sendto()'d straight away, so
* no need for setting for writing
* Limit the number of packets queued by this session
* to 4. Note that even though we try and limit this
* to 4 packets, the session could have more queued
* if the packets needed to be fragmented
* (XXX <= 4 ?)
*/
if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
}
}
/*
* ICMP sockets
*/
for (so = slirp->icmp.so_next; so != &slirp->icmp;
so = so_next) {
so_next = so->so_next;
/*
* See if it's timed out
*/
if (so->so_expire) {
if (so->so_expire <= curtime) {
icmp_detach(so);
continue;
} else {
do_slowtimo = 1; /* Let socket expire */
}
}
if (so->so_state & SS_ISFCONNECTED) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
}
/*
* See if it's timed out
*/
if (so->so_expire) {
if (so->so_expire <= curtime) {
udp_detach(so);
continue;
} else {
slirp->do_slowtimo = true; /* Let socket expire */
}
}
}
*pnfds = nfds;
/*
* When UDP packets are received from over the
* link, they're sendto()'d straight away, so
* no need for setting for writing
* Limit the number of packets queued by this session
* to 4. Note that even though we try and limit this
* to 4 packets, the session could have more queued
* if the packets needed to be fragmented
* (XXX <= 4 ?)
*/
if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
}
}
/*
* ICMP sockets
*/
for (so = slirp->icmp.so_next; so != &slirp->icmp;
so = so_next) {
so_next = so->so_next;
/*
* See if it's timed out
*/
if (so->so_expire) {
if (so->so_expire <= curtime) {
icmp_detach(so);
continue;
} else {
slirp->do_slowtimo = true; /* Let socket expire */
}
}
if (so->so_state & SS_ISFCONNECTED) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
}
}
}
slirp_update_timeout(timeout);
*pnfds = nfds;
}
void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
@ -427,177 +455,186 @@ void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds,
curtime = bx_pc_system.time_usec() / 1000;
QTAILQ_FOREACH(slirp, &slirp_instances, entry) {
/*
* See if anything has timed out
*/
if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
tcp_fasttimo(slirp);
time_fasttimo = 0;
}
if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
ip_slowtimo(slirp);
tcp_slowtimo(slirp);
last_slowtimo = curtime;
}
/*
* See if anything has timed out
*/
if (slirp->time_fasttimo &&
((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) {
tcp_fasttimo(slirp);
slirp->time_fasttimo = 0;
}
if (slirp->do_slowtimo &&
((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) {
ip_slowtimo(slirp);
tcp_slowtimo(slirp);
slirp->last_slowtimo = curtime;
}
/*
* Check sockets
*/
if (!select_error) {
/*
* Check TCP sockets
*/
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
/*
* FD_ISSET is meaningless on these sockets
* (and they can crash the program)
*/
if (so->so_state & SS_NOFDREF || so->s == -1)
continue;
/*
* Check for URG data
* This will soread as well, so no need to
* test for readfds below if this succeeds
*/
if (FD_ISSET(so->s, xfds))
sorecvoob(so);
/*
* Check sockets for reading
*/
else if (FD_ISSET(so->s, readfds)) {
/*
* Check for incoming connections
*/
if (so->so_state & SS_FACCEPTCONN) {
tcp_connect(so);
continue;
} /* else */
ret = soread(so);
/* Output it if we read something */
if (ret > 0)
tcp_output(sototcpcb(so));
}
/*
* Check sockets for writing
*/
if (FD_ISSET(so->s, writefds)) {
/*
* Check for non-blocking, still-connecting sockets
*/
if (so->so_state & SS_ISFCONNECTING) {
/* Connected */
so->so_state &= ~SS_ISFCONNECTING;
ret = send(so->s, (const char*) &ret, 0, 0);
if (ret < 0) {
/* XXXXX Must fix, zero bytes is a NOP */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue;
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
}
/* else so->so_state &= ~SS_ISFCONNECTING; */
/*
* Continue tcp_input
*/
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
/* continue; */
} else
ret = sowrite(so);
/*
* XXXXX If we wrote something (a lot), there
* could be a need for a window update.
* In the worst case, the remote will send
* a window probe to get things going again
*/
}
/*
* Probe a still-connecting, non-blocking socket
* to check if it's still alive
*/
#ifdef PROBE_CONN
if (so->so_state & SS_ISFCONNECTING) {
ret = qemu_recv(so->s, &ret, 0, 0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue; /* Still connecting, continue */
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
/* tcp_input will take care of it */
} else {
ret = send(so->s, (const char*)&ret, 0,0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue;
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
} else
so->so_state &= ~SS_ISFCONNECTING;
}
tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
} /* SS_ISFCONNECTING */
#endif
}
/*
* Now UDP sockets.
* Incoming packets are sent straight away, they're not buffered.
* Incoming UDP data isn't buffered either.
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
if (so->s != -1 && FD_ISSET(so->s, readfds)) {
sorecvfrom(so);
}
}
/*
* Check sockets
*/
if (!select_error) {
/*
* Check TCP sockets
*/
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
/*
* Check incoming ICMP relies.
* FD_ISSET is meaningless on these sockets
* (and they can crash the program)
*/
for (so = slirp->icmp.so_next; so != &slirp->icmp;
so = so_next) {
so_next = so->so_next;
if (so->so_state & SS_NOFDREF || so->s == -1) {
continue;
}
/*
* Check for URG data
* This will soread as well, so no need to
* test for readfds below if this succeeds
*/
if (FD_ISSET(so->s, xfds)) {
sorecvoob(so);
}
/*
* Check sockets for reading
*/
else if (FD_ISSET(so->s, readfds)) {
/*
* Check for incoming connections
*/
if (so->so_state & SS_FACCEPTCONN) {
tcp_connect(so);
continue;
} /* else */
ret = soread(so);
/* Output it if we read something */
if (ret > 0) {
tcp_output(sototcpcb(so));
}
}
/*
* Check sockets for writing
*/
if (FD_ISSET(so->s, writefds)) {
/*
* Check for non-blocking, still-connecting sockets
*/
if (so->so_state & SS_ISFCONNECTING) {
/* Connected */
so->so_state &= ~SS_ISFCONNECTING;
ret = send(so->s, (const char*) &ret, 0, 0);
if (ret < 0) {
/* XXXXX Must fix, zero bytes is a NOP */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN) {
continue;
}
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
}
/* else so->so_state &= ~SS_ISFCONNECTING; */
/*
* Continue tcp_input
*/
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
/* continue; */
} else {
ret = sowrite(so);
}
/*
* XXXXX If we wrote something (a lot), there
* could be a need for a window update.
* In the worst case, the remote will send
* a window probe to get things going again
*/
}
/*
* Probe a still-connecting, non-blocking socket
* to check if it's still alive
*/
#ifdef PROBE_CONN
if (so->so_state & SS_ISFCONNECTING) {
ret = qemu_recv(so->s, &ret, 0, 0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN) {
continue; /* Still connecting, continue */
}
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
/* tcp_input will take care of it */
} else {
ret = send(so->s, (const char*)&ret, 0, 0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN) {
continue;
}
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
} else {
so->so_state &= ~SS_ISFCONNECTING;
}
}
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
} /* SS_ISFCONNECTING */
#endif
}
/*
* Now UDP sockets.
* Incoming packets are sent straight away, they're not buffered.
* Incoming UDP data isn't buffered either.
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
if (so->s != -1 && FD_ISSET(so->s, readfds)) {
sorecvfrom(so);
}
}
/*
* Check incoming ICMP relies.
*/
for (so = slirp->icmp.so_next; so != &slirp->icmp;
so = so_next) {
so_next = so->so_next;
if (so->s != -1 && FD_ISSET(so->s, readfds)) {
icmp_receive(so);
}
}
}
}
}
if_start(slirp);
}
/* clear global file descriptor sets.
* these reside on the stack in vl.c
* so they're unusable if we're not in
* slirp_select_fill or slirp_select_poll.
*/
global_readfds = NULL;
global_writefds = NULL;
global_xfds = NULL;
/* clear global file descriptor sets.
* these reside on the stack in vl.c
* so they're unusable if we're not in
* slirp_select_fill or slirp_select_poll.
*/
global_readfds = NULL;
global_writefds = NULL;
global_xfds = NULL;
}
static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)

View File

@ -209,6 +209,9 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr,
struct Slirp {
QTAILQ_ENTRY(Slirp) entry;
u_int time_fasttimo;
u_int last_slowtimo;
bool do_slowtimo;
/* virtual network configuration */
struct in_addr vnetwork_addr;
@ -221,7 +224,6 @@ struct Slirp {
char client_hostname[33];
int restricted;
struct timeval tt;
struct ex_list *exec_list;
/* mbuf states */

View File

@ -338,9 +338,9 @@ int tcp_fconnect(struct socket *so)
qemu_set_nonblock(s);
opt = 1;
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt, sizeof(opt));
opt = 1;
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt, sizeof(opt));
addr.sin_family = AF_INET;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
@ -427,23 +427,29 @@ void tcp_connect(struct socket *inso)
}
qemu_set_nonblock(s);
opt = 1;
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt, sizeof(int));
opt = 1;
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt, sizeof(int));
opt = 1;
setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int));
setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt, sizeof(int));
so->so_fport = addr.sin_port;
so->so_faddr = addr.sin_addr;
/* Translate connections from localhost to the real hostname */
if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
so->so_faddr = slirp->vhost_addr;
if (so->so_faddr.s_addr == 0 ||
(so->so_faddr.s_addr & loopback_mask) ==
(loopback_addr.s_addr & loopback_mask)) {
so->so_faddr = slirp->vhost_addr;
}
/* Close the accept() socket, set right state */
if (inso->so_state & SS_FACCEPTONCE) {
closesocket(so->s); /* If we only accept once, close the accept() socket */
so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
/* if it's not FACCEPTONCE, it's already NOFDREF */
/* If we only accept once, close the accept() socket */
closesocket(so->s);
/* Don't select it yet, even though we have an FD */
/* if it's not FACCEPTONCE, it's already NOFDREF */
so->so_state = SS_NOFDREF;
}
so->s = s;
so->so_state |= SS_INCOMING;
@ -642,7 +648,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
n4 = (laddr & 0xff);
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
m->m_len += snprintf(bptr, m->m_size - m->m_len,
"ORT %d,%d,%d,%d,%d,%d\r\n%s",
n1, n2, n3, n4, n5, n6, x==7?buff:"");
return 1;
@ -675,7 +681,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
n4 = (laddr & 0xff);
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
m->m_len += snprintf(bptr, m->m_size - m->m_len,
"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
n1, n2, n3, n4, n5, n6, x==7?buff:"");
@ -701,7 +707,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
(so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
htons(lport), SS_FACCEPTONCE)) != NULL)
m->m_len = snprintf(m->m_data, m->m_hdr.mh_size, "%d",
m->m_len = snprintf(m->m_data, m->m_size, "%d",
ntohs(so->so_fport)) + 1;
return 1;
@ -721,7 +727,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
m->m_len += snprintf(bptr, m->m_size,
"DCC CHAT chat %lu %u%c\n",
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), 1);
@ -732,7 +738,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
m->m_len += snprintf(bptr, m->m_size,
"DCC SEND %s %lu %u %u%c\n", buff,
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), n1, 1);
@ -743,7 +749,7 @@ tcp_emu(struct socket *so, struct mbuf *m)
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
m->m_len += snprintf(bptr, m->m_size,
"DCC MOVE %s %lu %u %u%c\n", buff,
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), n1, 1);

View File

@ -36,6 +36,10 @@ static inline void tftp_session_update(struct tftp_session *spt)
static void tftp_session_terminate(struct tftp_session *spt)
{
if (spt->fd >= 0) {
close(spt->fd);
spt->fd = -1;
}
free(spt->filename);
spt->slirp = NULL;
}
@ -53,7 +57,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
/* sessions time out after 5 inactive seconds */
if ((int)(curtime - spt->timestamp) > 5000) {
free(spt->filename);
tftp_session_terminate(spt);
goto found;
}
}
@ -63,6 +67,7 @@ static int tftp_session_allocate(Slirp *slirp, struct tftp_t *tp)
found:
memset(spt, 0, sizeof(*spt));
memcpy(&spt->client_ip, &tp->ip.ip_src, sizeof(spt->client_ip));
spt->fd = -1;
spt->client_port = tp->udp.uh_sport;
spt->slirp = slirp;
@ -91,37 +96,36 @@ static int tftp_session_find(Slirp *slirp, struct tftp_t *tp)
return -1;
}
static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr,
static int tftp_read_data(struct tftp_session *spt, uint32_t block_nr,
uint8_t *buf, int len)
{
int fd;
int bytes_read = 0;
int bytes_read = 0;
fd = open(spt->filename, O_RDONLY | O_BINARY);
if (spt->fd < 0) {
spt->fd = open(spt->filename, O_RDONLY | O_BINARY);
}
if (fd < 0) {
return -1;
}
if (spt->fd < 0) {
return -1;
}
if (len) {
lseek(fd, block_nr * 512, SEEK_SET);
if (len) {
lseek(spt->fd, block_nr * 512, SEEK_SET);
bytes_read = read(fd, buf, len);
}
bytes_read = read(spt->fd, buf, len);
}
close(fd);
return bytes_read;
return bytes_read;
}
static int tftp_send_oack(struct tftp_session *spt,
const char *key, uint32_t value,
const char *keys[], uint32_t values[], int nb,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int n = 0;
int i, n = 0;
m = m_get(spt->slirp);
@ -135,10 +139,12 @@ static int tftp_send_oack(struct tftp_session *spt,
m->m_data += sizeof(struct udpiphdr);
tp->tp_op = htons(TFTP_OACK);
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
key) + 1;
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
value) + 1;
for (i = 0; i < nb; i++) {
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s",
keys[i]) + 1;
n += snprintf(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u",
values[i]) + 1;
}
saddr.sin_addr = recv_tp->ip.ip_dst;
saddr.sin_port = recv_tp->udp.uh_dport;
@ -192,23 +198,18 @@ out:
tftp_session_terminate(spt);
}
static int tftp_send_data(struct tftp_session *spt,
uint16_t block_nr,
struct tftp_t *recv_tp)
static void tftp_send_next_block(struct tftp_session *spt,
struct tftp_t *recv_tp)
{
struct sockaddr_in saddr, daddr;
struct mbuf *m;
struct tftp_t *tp;
int nobytes;
if (block_nr < 1) {
return -1;
}
m = m_get(spt->slirp);
if (!m) {
return -1;
return;
}
memset(m->m_data, 0, m->m_size);
@ -218,7 +219,7 @@ static int tftp_send_data(struct tftp_session *spt,
m->m_data += sizeof(struct udpiphdr);
tp->tp_op = htons(TFTP_DATA);
tp->x.tp_data.tp_block_nr = htons(block_nr);
tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
saddr.sin_addr = recv_tp->ip.ip_dst;
saddr.sin_port = recv_tp->udp.uh_dport;
@ -226,7 +227,7 @@ static int tftp_send_data(struct tftp_session *spt,
daddr.sin_addr = spt->client_ip;
daddr.sin_port = spt->client_port;
nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf, 512);
if (nobytes < 0) {
m_free(m);
@ -235,7 +236,7 @@ static int tftp_send_data(struct tftp_session *spt,
tftp_send_error(spt, 1, "File not found", tp);
return -1;
return;
}
m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
@ -250,7 +251,7 @@ static int tftp_send_data(struct tftp_session *spt,
tftp_session_terminate(spt);
}
return 0;
spt->block_nr++;
}
static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
@ -259,6 +260,9 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
int s, k;
size_t prefix_len;
char *req_fname;
const char *option_name[2];
uint32_t option_value[2];
unsigned nb_options = 0;
/* check if a session already exists and if so terminate it */
s = tftp_session_find(slirp, tp);
@ -336,7 +340,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
return;
}
while (k < pktlen) {
while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) {
const char *key, *value;
key = &tp->x.tp_buf[k];
@ -363,12 +367,32 @@ static void tftp_handle_rrq(Slirp *slirp, struct tftp_t *tp, int pktlen)
}
}
tftp_send_oack(spt, "tsize", tsize, tp);
return;
option_name[nb_options] = "tsize";
option_value[nb_options] = tsize;
nb_options++;
} else if (strcasecmp(key, "blksize") == 0) {
int blksize = atoi(value);
/* If blksize option is bigger than what we will
* emit, accept the option with our packet size.
* Otherwise, simply do as we didn't see the option.
*/
if (blksize >= 512) {
option_name[nb_options] = "blksize";
option_value[nb_options] = 512;
nb_options++;
}
}
}
tftp_send_data(spt, 1, tp);
if (nb_options > 0) {
assert(nb_options <= ARRAY_SIZE(option_name));
tftp_send_oack(spt, option_name, option_value, nb_options, tp);
return;
}
spt->block_nr = 0;
tftp_send_next_block(spt, tp);
}
static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
@ -381,11 +405,7 @@ static void tftp_handle_ack(Slirp *slirp, struct tftp_t *tp, int pktlen)
return;
}
if (tftp_send_data(&slirp->tftp_sessions[s],
ntohs(tp->x.tp_data.tp_block_nr) + 1,
tp) < 0) {
return;
}
tftp_send_next_block(&slirp->tftp_sessions[s], tp);
}
static void tftp_handle_error(Slirp *slirp, struct tftp_t *tp, int pktlen)

View File

@ -1,4 +1,6 @@
/* tftp defines */
#ifndef SLIRP_TFTP_H
#define SLIRP_TFTP_H 1
#define TFTP_SESSIONS_MAX 3
@ -33,11 +35,15 @@ struct tftp_t {
struct tftp_session {
Slirp *slirp;
char *filename;
int fd;
struct in_addr client_ip;
uint16_t client_port;
uint32_t block_nr;
int timestamp;
};
void tftp_input(struct mbuf *m);
#endif