Redirect slirp traffic to/from qemu character device (Gleb Natapov)

Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6240 c046a42c-6fe2-441c-8c8c-71466251a162
master
aliguori 2009-01-08 19:18:21 +00:00
parent 4dda406337
commit e1c5a2b334
8 changed files with 156 additions and 25 deletions

View File

@ -20,13 +20,16 @@ void slirp_output(const uint8_t *pkt, int pkt_len);
int slirp_redir(int is_udp, int host_port,
struct in_addr guest_addr, int guest_port);
int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
int slirp_add_exec(int do_pty, const void *args, int addr_low_byte,
int guest_port);
extern const char *tftp_prefix;
extern char slirp_hostname[33];
void slirp_stats(void);
void slirp_socket_recv(int addr_low_byte, int guest_port, const uint8_t *buf,
int size);
size_t slirp_socket_can_recv(int addr_low_byte, int guest_port);
#ifdef __cplusplus
}

View File

@ -51,3 +51,4 @@ extern uint8_t client_ethaddr[6];
#endif
void if_encap(const uint8_t *ip_data, int ip_data_len);
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);

View File

@ -169,7 +169,7 @@ add_exec(ex_ptr, do_pty, exec, addr, port)
(*ex_ptr)->ex_fport = port;
(*ex_ptr)->ex_addr = addr;
(*ex_ptr)->ex_pty = do_pty;
(*ex_ptr)->ex_exec = strdup(exec);
(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
(*ex_ptr)->ex_next = tmp_ptr;
return 0;
}

View File

@ -108,7 +108,7 @@ sbappend(so, m)
* ottherwise it'll arrive out of order, and hence corrupt
*/
if (!so->so_rcv.sb_cc)
ret = send(so->s, m->m_data, m->m_len, 0);
ret = slirp_send(so, m->m_data, m->m_len, 0);
if (ret <= 0) {
/*

View File

@ -21,6 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu-common.h"
#include "slirp.h"
/* host address */
@ -736,9 +737,69 @@ int slirp_redir(int is_udp, int host_port,
return 0;
}
int slirp_add_exec(int do_pty, const char *args, int addr_low_byte,
int slirp_add_exec(int do_pty, const void *args, int addr_low_byte,
int guest_port)
{
return add_exec(&exec_list, do_pty, (char *)args,
addr_low_byte, htons(guest_port));
}
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
{
if (so->s == -1 && so->extra) {
qemu_chr_write(so->extra, buf, len);
return len;
}
return send(so->s, buf, len, flags);
}
static struct socket *slirp_find_ctl_socket(int addr_low_byte, int guest_port)
{
struct socket *so;
for (so = tcb.so_next; so != &tcb; so = so->so_next) {
if ((so->so_faddr.s_addr & htonl(0xffffff00)) ==
special_addr.s_addr
&& (ntohl(so->so_faddr.s_addr) & 0xff) ==
addr_low_byte
&& htons(so->so_fport) == guest_port)
return so;
}
return NULL;
}
size_t slirp_socket_can_recv(int addr_low_byte, int guest_port)
{
struct iovec iov[2];
struct socket *so;
if (!link_up)
return 0;
so = slirp_find_ctl_socket(addr_low_byte, guest_port);
if (!so || so->so_state & SS_NOFDREF)
return 0;
if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
return 0;
return sopreprbuf(so, iov, NULL);
}
void slirp_socket_recv(int addr_low_byte, int guest_port, const uint8_t *buf,
int size)
{
int ret;
struct socket *so = slirp_find_ctl_socket(addr_low_byte, guest_port);
if (!so)
return;
ret = soreadbuf(so, buf, size);
if (ret > 0)
tcp_output(sototcpcb(so));
}

View File

@ -5,13 +5,13 @@
* terms and conditions of the copyright.
*/
#include "qemu-common.h"
#define WANT_SYS_IOCTL_H
#include <slirp.h>
#include "ip_icmp.h"
#ifdef __sun__
#include <sys/filio.h>
#endif
#include "qemu-common.h"
static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so);
@ -91,31 +91,21 @@ sofree(so)
free(so);
}
/*
* Read from so's socket into sb_snd, updating all relevant sbuf fields
* NOTE: This will only be called if it is select()ed for reading, so
* a read() of 0 (or less) means it's disconnected
*/
int
soread(so)
struct socket *so;
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
{
int n, nn, lss, total;
int n, lss, total;
struct sbuf *sb = &so->so_snd;
int len = sb->sb_datalen - sb->sb_cc;
struct iovec iov[2];
int mss = so->so_tcpcb->t_maxseg;
DEBUG_CALL("soread");
DEBUG_CALL("sopreprbuf");
DEBUG_ARG("so = %lx", (long )so);
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
len = sb->sb_datalen - sb->sb_cc;
if (len <= 0)
return 0;
iov[0].iov_base = sb->sb_wptr;
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
@ -156,6 +146,33 @@ soread(so)
n = 1;
}
}
if (np)
*np = n;
return iov[0].iov_len + (n - 1) * iov[1].iov_len;
}
/*
* Read from so's socket into sb_snd, updating all relevant sbuf fields
* NOTE: This will only be called if it is select()ed for reading, so
* a read() of 0 (or less) means it's disconnected
*/
int
soread(so)
struct socket *so;
{
int n, nn;
struct sbuf *sb = &so->so_snd;
struct iovec iov[2];
DEBUG_CALL("soread");
DEBUG_ARG("so = %lx", (long )so);
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
sopreprbuf(so, iov, &n);
#ifdef HAVE_READV
nn = readv(so->s, (struct iovec *)iov, n);
@ -202,6 +219,48 @@ soread(so)
return nn;
}
int soreadbuf(struct socket *so, const char *buf, int size)
{
int n, nn, copy = size;
struct sbuf *sb = &so->so_snd;
struct iovec iov[2];
DEBUG_CALL("soreadbuf");
DEBUG_ARG("so = %lx", (long )so);
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
if (sopreprbuf(so, iov, &n) < size)
goto err;
nn = MIN(iov[0].iov_len, copy);
memcpy(iov[0].iov_base, buf, nn);
copy -= nn;
buf += nn;
if (copy == 0)
goto done;
memcpy(iov[1].iov_base, buf, copy);
done:
/* Update fields */
sb->sb_cc += size;
sb->sb_wptr += size;
if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
sb->sb_wptr -= sb->sb_datalen;
return size;
err:
sofcantrcvmore(so);
tcp_sockclosed(sototcpcb(so));
fprintf(stderr, "soreadbuf buffer to small");
return -1;
}
/*
* Get urgent data
*
@ -255,7 +314,7 @@ sosendoob(so)
if (sb->sb_rptr < sb->sb_wptr) {
/* We can send it directly */
n = send(so->s, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
n = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
so->so_urgc -= n;
DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
@ -276,7 +335,7 @@ sosendoob(so)
so->so_urgc -= n;
len += n;
}
n = send(so->s, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
#ifdef DEBUG
if (n != len)
DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
@ -348,7 +407,7 @@ sowrite(so)
DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
#else
nn = send(so->s, iov[0].iov_base, iov[0].iov_len,0);
nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0);
#endif
/* This should never happen, but people tell me it does *shrug* */
if (nn < 0 && (errno == EAGAIN || errno == EINTR))
@ -365,7 +424,7 @@ sowrite(so)
#ifndef HAVE_READV
if (n == 2 && nn == iov[0].iov_len) {
int ret;
ret = send(so->s, iov[1].iov_base, iov[1].iov_len,0);
ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0);
if (ret > 0)
nn += ret;
}

View File

@ -87,5 +87,7 @@ void soisfconnecting _P((register struct socket *));
void soisfconnected _P((register struct socket *));
void soisfdisconnected _P((struct socket *));
void sofwdrain _P((struct socket *));
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
int soreadbuf(struct socket *so, const char *buf, int size);
#endif /* _SOCKET_H_ */

View File

@ -1281,6 +1281,11 @@ tcp_ctl(so)
for (ex_ptr = exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
if (ex_ptr->ex_fport == so->so_fport &&
command == ex_ptr->ex_addr) {
if (ex_ptr->ex_pty == 3) {
so->s = -1;
so->extra = ex_ptr->ex_exec;
return 1;
}
do_pty = ex_ptr->ex_pty;
goto do_exec;
}