-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1
 
 iQEcBAABAgAGBQJWswswAAoJEO8Ells5jWIRmuAIAKfexolRpauVFoMt2w69Yrk4
 0XhaAuSaazsfU06azXKjrchBUgXbw4Y6lw3tkTos4lnd8m1ovfAzSTS4q28rZ+Tf
 u5M06Fi13oyhEViGS4gt6gTwmYPTx2FTBDMCL1OZvka7GPbVsweQn0IS18j1Q2xL
 ps2kruNTad7mUa2EypuBugm3woL8kGupLUX63aWKmnvqobwFDNTKJLWiFn5eXlbg
 Zq7LxmC4R3A5K9rD8wN16ScaK3RH2x83DXaRoddtSIRwdldxG9ZCv2oFKPZrr6WA
 HsJIjurMTXhaRxNL3PsGMd/MbT7gmNF5muq8kZnkORmGxfMvi3RUuBdyhrq1I0w=
 =2Uz/
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging

# gpg: Signature made Thu 04 Feb 2016 08:26:24 GMT using RSA key ID 398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* remotes/jasowang/tags/net-pull-request:
  net/filter: Fix the output information for command 'info network'
  net: always walk through filters in reverse if traffic is egress
  net: netmap: use nm_open() to open netmap ports
  e1000: eliminate infinite loops on out-of-bounds transfer start
  slirp: Adding family argument to tcp_fconnect()
  slirp: Make udp_attach IPv6 compatible
  slirp: Add sockaddr_equal, make solookup family-agnostic
  slirp: Factorizing and cleaning solookup()
  slirp: Factorizing address translation
  slirp: Make Socket structure IPv6 compatible
  slirp: Adding address family switch for produced frames
  slirp: Generalizing and neutralizing ARP code
  slirp: goto bad in udp_input if sosendto fails
  cadence_gem: fix buffer overflow
  net: cadence_gem: check packet size in gem_recieve
  qemu-doc: Do not promote deprecated -smb and -redir options
  net/slirp: Tell the users when they are using deprecated options

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-02-04 14:17:11 +00:00
commit bac8e20367
24 changed files with 443 additions and 294 deletions

View File

@ -678,6 +678,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
} else { } else {
unsigned crc_val; unsigned crc_val;
if (size > sizeof(rxbuf) - sizeof(crc_val)) {
size = sizeof(rxbuf) - sizeof(crc_val);
}
bytes_to_copy = size;
/* The application wants the FCS field, which QEMU does not provide. /* The application wants the FCS field, which QEMU does not provide.
* We must try and calculate one. * We must try and calculate one.
*/ */
@ -863,6 +867,14 @@ static void gem_transmit(CadenceGEMState *s)
break; break;
} }
if (tx_desc_get_length(desc) > sizeof(tx_packet) - (p - tx_packet)) {
DB_PRINT("TX descriptor @ 0x%x too large: size 0x%x space 0x%x\n",
(unsigned)packet_desc_addr,
(unsigned)tx_desc_get_length(desc),
sizeof(tx_packet) - (p - tx_packet));
break;
}
/* Gather this fragment of the packet from "dma memory" to our contig. /* Gather this fragment of the packet from "dma memory" to our contig.
* buffer. * buffer.
*/ */

View File

@ -909,7 +909,8 @@ start_xmit(E1000State *s)
* bogus values to TDT/TDLEN. * bogus values to TDT/TDLEN.
* there's nothing too intelligent we could do about this. * there's nothing too intelligent we could do about this.
*/ */
if (s->mac_reg[TDH] == tdh_start) { if (s->mac_reg[TDH] == tdh_start ||
tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) {
DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n",
tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]);
break; break;
@ -1166,7 +1167,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt)
if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN])
s->mac_reg[RDH] = 0; s->mac_reg[RDH] = 0;
/* see comment in start_xmit; same here */ /* see comment in start_xmit; same here */
if (s->mac_reg[RDH] == rdh_start) { if (s->mac_reg[RDH] == rdh_start ||
rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) {
DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n",
rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]);
set_ics(s, 0, E1000_ICS_RXO); set_ics(s, 0, E1000_ICS_RXO);

View File

@ -55,7 +55,6 @@ struct NetFilterState {
char *netdev_id; char *netdev_id;
NetClientState *netdev; NetClientState *netdev;
NetFilterDirection direction; NetFilterDirection direction;
char info_str[256];
QTAILQ_ENTRY(NetFilterState) next; QTAILQ_ENTRY(NetFilterState) next;
}; };

View File

@ -92,7 +92,7 @@ struct NetClientState {
NetClientDestructor *destructor; NetClientDestructor *destructor;
unsigned int queue_index; unsigned int queue_index;
unsigned rxfilter_notify_enabled:1; unsigned rxfilter_notify_enabled:1;
QTAILQ_HEAD(, NetFilterState) filters; QTAILQ_HEAD(NetFilterHead, NetFilterState) filters;
}; };
typedef struct NICState { typedef struct NICState {

View File

@ -15,7 +15,6 @@
#include "net/vhost_net.h" #include "net/vhost_net.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "qapi/string-output-visitor.h"
ssize_t qemu_netfilter_receive(NetFilterState *nf, ssize_t qemu_netfilter_receive(NetFilterState *nf,
NetFilterDirection direction, NetFilterDirection direction,
@ -34,6 +33,22 @@ ssize_t qemu_netfilter_receive(NetFilterState *nf,
return 0; return 0;
} }
static NetFilterState *netfilter_next(NetFilterState *nf,
NetFilterDirection dir)
{
NetFilterState *next;
if (dir == NET_FILTER_DIRECTION_TX) {
/* forward walk through filters */
next = QTAILQ_NEXT(nf, next);
} else {
/* reverse order */
next = QTAILQ_PREV(nf, NetFilterHead, next);
}
return next;
}
ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
unsigned flags, unsigned flags,
const struct iovec *iov, const struct iovec *iov,
@ -43,7 +58,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
int ret = 0; int ret = 0;
int direction; int direction;
NetFilterState *nf = opaque; NetFilterState *nf = opaque;
NetFilterState *next = QTAILQ_NEXT(nf, next); NetFilterState *next = NULL;
if (!sender || !sender->peer) { if (!sender || !sender->peer) {
/* no receiver, or sender been deleted, no need to pass it further */ /* no receiver, or sender been deleted, no need to pass it further */
@ -61,6 +76,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
direction = nf->direction; direction = nf->direction;
} }
next = netfilter_next(nf, direction);
while (next) { while (next) {
/* /*
* if qemu_netfilter_pass_to_next been called, means that * if qemu_netfilter_pass_to_next been called, means that
@ -73,7 +89,7 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
if (ret) { if (ret) {
return ret; return ret;
} }
next = QTAILQ_NEXT(next, next); next = netfilter_next(next, direction);
} }
/* /*
@ -135,10 +151,6 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); NetFilterClass *nfc = NETFILTER_GET_CLASS(uc);
int queues; int queues;
Error *local_err = NULL; Error *local_err = NULL;
char *str, *info;
ObjectProperty *prop;
ObjectPropertyIterator iter;
StringOutputVisitor *ov;
if (!nf->netdev_id) { if (!nf->netdev_id) {
error_setg(errp, "Parameter 'netdev' is required"); error_setg(errp, "Parameter 'netdev' is required");
@ -172,23 +184,6 @@ static void netfilter_complete(UserCreatable *uc, Error **errp)
} }
} }
QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
/* generate info str */
object_property_iter_init(&iter, OBJECT(nf));
while ((prop = object_property_iter_next(&iter))) {
if (!strcmp(prop->name, "type")) {
continue;
}
ov = string_output_visitor_new(false);
object_property_get(OBJECT(nf), string_output_get_visitor(ov),
prop->name, errp);
str = string_output_get_string(ov);
string_output_visitor_cleanup(ov);
info = g_strdup_printf(",%s=%s", prop->name, str);
g_strlcat(nf->info_str, info, sizeof(nf->info_str));
g_free(str);
g_free(info);
}
} }
static void netfilter_finalize(Object *obj) static void netfilter_finalize(Object *obj)

View File

@ -45,6 +45,7 @@
#include "qapi/dealloc-visitor.h" #include "qapi/dealloc-visitor.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "net/filter.h" #include "net/filter.h"
#include "qapi/string-output-visitor.h"
/* Net bridge is currently not supported for W32. */ /* Net bridge is currently not supported for W32. */
#if !defined(_WIN32) #if !defined(_WIN32)
@ -580,11 +581,21 @@ static ssize_t filter_receive_iov(NetClientState *nc,
ssize_t ret = 0; ssize_t ret = 0;
NetFilterState *nf = NULL; NetFilterState *nf = NULL;
QTAILQ_FOREACH(nf, &nc->filters, next) { if (direction == NET_FILTER_DIRECTION_TX) {
ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, QTAILQ_FOREACH(nf, &nc->filters, next) {
iovcnt, sent_cb); ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
if (ret) { iovcnt, sent_cb);
return ret; if (ret) {
return ret;
}
}
} else {
QTAILQ_FOREACH_REVERSE(nf, &nc->filters, NetFilterHead, next) {
ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
iovcnt, sent_cb);
if (ret) {
return ret;
}
} }
} }
@ -1185,6 +1196,30 @@ void qmp_netdev_del(const char *id, Error **errp)
qemu_opts_del(opts); qemu_opts_del(opts);
} }
static void netfilter_print_info(Monitor *mon, NetFilterState *nf)
{
char *str;
ObjectProperty *prop;
ObjectPropertyIterator iter;
StringOutputVisitor *ov;
/* generate info str */
object_property_iter_init(&iter, OBJECT(nf));
while ((prop = object_property_iter_next(&iter))) {
if (!strcmp(prop->name, "type")) {
continue;
}
ov = string_output_visitor_new(false);
object_property_get(OBJECT(nf), string_output_get_visitor(ov),
prop->name, NULL);
str = string_output_get_string(ov);
string_output_visitor_cleanup(ov);
monitor_printf(mon, ",%s=%s", prop->name, str);
g_free(str);
}
monitor_printf(mon, "\n");
}
void print_net_client(Monitor *mon, NetClientState *nc) void print_net_client(Monitor *mon, NetClientState *nc)
{ {
NetFilterState *nf; NetFilterState *nf;
@ -1198,9 +1233,10 @@ void print_net_client(Monitor *mon, NetClientState *nc)
} }
QTAILQ_FOREACH(nf, &nc->filters, next) { QTAILQ_FOREACH(nf, &nc->filters, next) {
char *path = object_get_canonical_path_component(OBJECT(nf)); char *path = object_get_canonical_path_component(OBJECT(nf));
monitor_printf(mon, " - %s: type=%s%s\n", path,
object_get_typename(OBJECT(nf)), monitor_printf(mon, " - %s: type=%s", path,
nf->info_str); object_get_typename(OBJECT(nf)));
netfilter_print_info(mon, nf);
g_free(path); g_free(path);
} }
} }

View File

@ -39,21 +39,12 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/iov.h" #include "qemu/iov.h"
/* Private netmap device info. */
typedef struct NetmapPriv {
int fd;
size_t memsize;
void *mem;
struct netmap_if *nifp;
struct netmap_ring *rx;
struct netmap_ring *tx;
char fdname[PATH_MAX]; /* Normally "/dev/netmap". */
char ifname[IFNAMSIZ];
} NetmapPriv;
typedef struct NetmapState { typedef struct NetmapState {
NetClientState nc; NetClientState nc;
NetmapPriv me; struct nm_desc *nmd;
char ifname[IFNAMSIZ];
struct netmap_ring *tx;
struct netmap_ring *rx;
bool read_poll; bool read_poll;
bool write_poll; bool write_poll;
struct iovec iov[IOV_MAX]; struct iovec iov[IOV_MAX];
@ -90,44 +81,23 @@ pkt_copy(const void *_src, void *_dst, int l)
* Open a netmap device. We assume there is only one queue * Open a netmap device. We assume there is only one queue
* (which is the case for the VALE bridge). * (which is the case for the VALE bridge).
*/ */
static void netmap_open(NetmapPriv *me, Error **errp) static struct nm_desc *netmap_open(const NetdevNetmapOptions *nm_opts,
Error **errp)
{ {
int fd; struct nm_desc *nmd;
int err;
size_t l;
struct nmreq req; struct nmreq req;
me->fd = fd = open(me->fdname, O_RDWR);
if (fd < 0) {
error_setg_file_open(errp, errno, me->fdname);
return;
}
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
pstrcpy(req.nr_name, sizeof(req.nr_name), me->ifname);
req.nr_ringid = NETMAP_NO_TX_POLL;
req.nr_version = NETMAP_API;
err = ioctl(fd, NIOCREGIF, &req);
if (err) {
error_setg_errno(errp, errno, "Unable to register %s", me->ifname);
goto error;
}
l = me->memsize = req.nr_memsize;
me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); nmd = nm_open(nm_opts->ifname, &req, NETMAP_NO_TX_POLL,
if (me->mem == MAP_FAILED) { NULL);
error_setg_errno(errp, errno, "Unable to mmap netmap shared memory"); if (nmd == NULL) {
me->mem = NULL; error_setg_errno(errp, errno, "Failed to nm_open() %s",
goto error; nm_opts->ifname);
return NULL;
} }
me->nifp = NETMAP_IF(me->mem, req.nr_offset); return nmd;
me->tx = NETMAP_TXRING(me->nifp, 0);
me->rx = NETMAP_RXRING(me->nifp, 0);
return;
error:
close(me->fd);
} }
static void netmap_send(void *opaque); static void netmap_send(void *opaque);
@ -136,7 +106,7 @@ static void netmap_writable(void *opaque);
/* Set the event-loop handlers for the netmap backend. */ /* Set the event-loop handlers for the netmap backend. */
static void netmap_update_fd_handler(NetmapState *s) static void netmap_update_fd_handler(NetmapState *s)
{ {
qemu_set_fd_handler(s->me.fd, qemu_set_fd_handler(s->nmd->fd,
s->read_poll ? netmap_send : NULL, s->read_poll ? netmap_send : NULL,
s->write_poll ? netmap_writable : NULL, s->write_poll ? netmap_writable : NULL,
s); s);
@ -188,7 +158,7 @@ static ssize_t netmap_receive(NetClientState *nc,
const uint8_t *buf, size_t size) const uint8_t *buf, size_t size)
{ {
NetmapState *s = DO_UPCAST(NetmapState, nc, nc); NetmapState *s = DO_UPCAST(NetmapState, nc, nc);
struct netmap_ring *ring = s->me.tx; struct netmap_ring *ring = s->tx;
uint32_t i; uint32_t i;
uint32_t idx; uint32_t idx;
uint8_t *dst; uint8_t *dst;
@ -218,7 +188,7 @@ static ssize_t netmap_receive(NetClientState *nc,
ring->slot[i].flags = 0; ring->slot[i].flags = 0;
pkt_copy(buf, dst, size); pkt_copy(buf, dst, size);
ring->cur = ring->head = nm_ring_next(ring, i); ring->cur = ring->head = nm_ring_next(ring, i);
ioctl(s->me.fd, NIOCTXSYNC, NULL); ioctl(s->nmd->fd, NIOCTXSYNC, NULL);
return size; return size;
} }
@ -227,7 +197,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc,
const struct iovec *iov, int iovcnt) const struct iovec *iov, int iovcnt)
{ {
NetmapState *s = DO_UPCAST(NetmapState, nc, nc); NetmapState *s = DO_UPCAST(NetmapState, nc, nc);
struct netmap_ring *ring = s->me.tx; struct netmap_ring *ring = s->tx;
uint32_t last; uint32_t last;
uint32_t idx; uint32_t idx;
uint8_t *dst; uint8_t *dst;
@ -284,7 +254,7 @@ static ssize_t netmap_receive_iov(NetClientState *nc,
/* Now update ring->cur and ring->head. */ /* Now update ring->cur and ring->head. */
ring->cur = ring->head = i; ring->cur = ring->head = i;
ioctl(s->me.fd, NIOCTXSYNC, NULL); ioctl(s->nmd->fd, NIOCTXSYNC, NULL);
return iov_size(iov, iovcnt); return iov_size(iov, iovcnt);
} }
@ -301,7 +271,7 @@ static void netmap_send_completed(NetClientState *nc, ssize_t len)
static void netmap_send(void *opaque) static void netmap_send(void *opaque)
{ {
NetmapState *s = opaque; NetmapState *s = opaque;
struct netmap_ring *ring = s->me.rx; struct netmap_ring *ring = s->rx;
/* Keep sending while there are available packets into the netmap /* Keep sending while there are available packets into the netmap
RX ring and the forwarding path towards the peer is open. */ RX ring and the forwarding path towards the peer is open. */
@ -349,10 +319,8 @@ static void netmap_cleanup(NetClientState *nc)
qemu_purge_queued_packets(nc); qemu_purge_queued_packets(nc);
netmap_poll(nc, false); netmap_poll(nc, false);
munmap(s->me.mem, s->me.memsize); nm_close(s->nmd);
close(s->me.fd); s->nmd = NULL;
s->me.fd = -1;
} }
/* Offloading manipulation support callbacks. */ /* Offloading manipulation support callbacks. */
@ -383,17 +351,17 @@ static void netmap_set_vnet_hdr_len(NetClientState *nc, int len)
struct nmreq req; struct nmreq req;
/* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header /* Issue a NETMAP_BDG_VNET_HDR command to change the virtio-net header
* length for the netmap adapter associated to 'me->ifname'. * length for the netmap adapter associated to 's->ifname'.
*/ */
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
pstrcpy(req.nr_name, sizeof(req.nr_name), s->me.ifname); pstrcpy(req.nr_name, sizeof(req.nr_name), s->ifname);
req.nr_version = NETMAP_API; req.nr_version = NETMAP_API;
req.nr_cmd = NETMAP_BDG_VNET_HDR; req.nr_cmd = NETMAP_BDG_VNET_HDR;
req.nr_arg1 = len; req.nr_arg1 = len;
err = ioctl(s->me.fd, NIOCREGIF, &req); err = ioctl(s->nmd->fd, NIOCREGIF, &req);
if (err) { if (err) {
error_report("Unable to execute NETMAP_BDG_VNET_HDR on %s: %s", error_report("Unable to execute NETMAP_BDG_VNET_HDR on %s: %s",
s->me.ifname, strerror(errno)); s->ifname, strerror(errno));
} else { } else {
/* Keep track of the current length. */ /* Keep track of the current length. */
s->vnet_hdr_len = len; s->vnet_hdr_len = len;
@ -437,16 +405,12 @@ int net_init_netmap(const NetClientOptions *opts,
const char *name, NetClientState *peer, Error **errp) const char *name, NetClientState *peer, Error **errp)
{ {
const NetdevNetmapOptions *netmap_opts = opts->u.netmap; const NetdevNetmapOptions *netmap_opts = opts->u.netmap;
struct nm_desc *nmd;
NetClientState *nc; NetClientState *nc;
Error *err = NULL; Error *err = NULL;
NetmapPriv me;
NetmapState *s; NetmapState *s;
pstrcpy(me.fdname, sizeof(me.fdname), nmd = netmap_open(netmap_opts, &err);
netmap_opts->has_devname ? netmap_opts->devname : "/dev/netmap");
/* Set default name for the port if not supplied. */
pstrcpy(me.ifname, sizeof(me.ifname), netmap_opts->ifname);
netmap_open(&me, &err);
if (err) { if (err) {
error_propagate(errp, err); error_propagate(errp, err);
return -1; return -1;
@ -454,8 +418,11 @@ int net_init_netmap(const NetClientOptions *opts,
/* Create the object. */ /* Create the object. */
nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name); nc = qemu_new_net_client(&net_netmap_info, peer, "netmap", name);
s = DO_UPCAST(NetmapState, nc, nc); s = DO_UPCAST(NetmapState, nc, nc);
s->me = me; s->nmd = nmd;
s->tx = NETMAP_TXRING(nmd->nifp, 0);
s->rx = NETMAP_RXRING(nmd->nifp, 0);
s->vnet_hdr_len = 0; s->vnet_hdr_len = 0;
pstrcpy(s->ifname, sizeof(s->ifname), netmap_opts->ifname);
netmap_read_poll(s, true); /* Initially only poll for reads. */ netmap_read_poll(s, true); /* Initially only poll for reads. */
return 0; return 0;

View File

@ -784,6 +784,9 @@ int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret
return 0; return 0;
} }
error_report("The '-net channel' option is deprecated. "
"Please use '-netdev user,guestfwd=...' instead.");
/* handle legacy -net channel,port:chr */ /* handle legacy -net channel,port:chr */
optarg += strlen("channel,"); optarg += strlen("channel,");

View File

@ -40,6 +40,7 @@
#include "net/slirp.h" #include "net/slirp.h"
#include "qemu-options.h" #include "qemu-options.h"
#include "qemu/rcu.h" #include "qemu/rcu.h"
#include "qemu/error-report.h"
#ifdef CONFIG_LINUX #ifdef CONFIG_LINUX
#include <sys/prctl.h> #include <sys/prctl.h>
@ -139,6 +140,8 @@ void os_parse_cmd_args(int index, const char *optarg)
switch (index) { switch (index) {
#ifdef CONFIG_SLIRP #ifdef CONFIG_SLIRP
case QEMU_OPTION_smb: case QEMU_OPTION_smb:
error_report("The -smb option is deprecated. "
"Please use '-netdev user,smb=...' instead.");
if (net_slirp_smb(optarg) < 0) if (net_slirp_smb(optarg) < 0)
exit(1); exit(1);
break; break;

View File

@ -1237,9 +1237,9 @@ echo 100 100 > /proc/sys/net/ipv4/ping_group_range
When using the built-in TFTP server, the router is also the TFTP When using the built-in TFTP server, the router is also the TFTP
server. server.
When using the @option{-redir} option, TCP or UDP connections can be When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP
redirected from the host to the guest. It allows for example to connections can be redirected from the host to the guest. It allows for
redirect X11, telnet or SSH connections. example to redirect X11, telnet or SSH connections.
@subsection Connecting VLANs between QEMU instances @subsection Connecting VLANs between QEMU instances
@ -1889,7 +1889,8 @@ correctly instructs QEMU to shutdown at the appropriate moment.
@subsubsection Share a directory between Unix and Windows @subsubsection Share a directory between Unix and Windows
See @ref{sec_invocation} about the help of the option @option{-smb}. See @ref{sec_invocation} about the help of the option
@option{'-netdev user,smb=...'}.
@subsubsection Windows XP security problem @subsubsection Windows XP security problem

View File

@ -325,7 +325,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
m->m_len = sizeof(struct bootp_t) - m->m_len = sizeof(struct bootp_t) -
sizeof(struct ip) - sizeof(struct udphdr); sizeof(struct ip) - sizeof(struct udphdr);
udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
} }
void bootp_input(struct mbuf *m) void bootp_input(struct mbuf *m)

View File

@ -157,12 +157,12 @@ icmp_input(struct mbuf *m, int hlen)
goto freeit; goto freeit;
} else { } else {
struct socket *so; struct socket *so;
struct sockaddr_in addr; struct sockaddr_storage addr;
if ((so = socreate(slirp)) == NULL) goto freeit; if ((so = socreate(slirp)) == NULL) goto freeit;
if (icmp_send(so, m, hlen) == 0) { if (icmp_send(so, m, hlen) == 0) {
return; return;
} }
if(udp_attach(so) == -1) { if (udp_attach(so, AF_INET) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno))); errno,strerror(errno)));
sofree(so); sofree(so);
@ -170,8 +170,10 @@ icmp_input(struct mbuf *m, int hlen)
goto end_error; goto end_error;
} }
so->so_m = m; so->so_m = m;
so->so_ffamily = AF_INET;
so->so_faddr = ip->ip_dst; so->so_faddr = ip->ip_dst;
so->so_fport = htons(7); so->so_fport = htons(7);
so->so_lfamily = AF_INET;
so->so_laddr = ip->ip_src; so->so_laddr = ip->ip_src;
so->so_lport = htons(9); so->so_lport = htons(9);
so->so_iptos = ip->ip_tos; so->so_iptos = ip->ip_tos;
@ -179,20 +181,9 @@ icmp_input(struct mbuf *m, int hlen)
so->so_state = SS_ISFCONNECTED; so->so_state = SS_ISFCONNECTED;
/* Send the packet */ /* Send the packet */
addr.sin_family = AF_INET; addr = so->fhost.ss;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == sotranslate_out(so, &addr);
slirp->vnetwork_addr.s_addr) {
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else {
addr.sin_addr = so->so_faddr;
}
addr.sin_port = so->so_fport;
if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1) { (struct sockaddr *)&addr, sizeof(addr)) == -1) {
DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",

View File

@ -91,7 +91,7 @@ m_get(Slirp *slirp)
m->m_len = 0; m->m_len = 0;
m->m_nextpkt = NULL; m->m_nextpkt = NULL;
m->m_prevpkt = NULL; m->m_prevpkt = NULL;
m->arp_requested = false; m->resolution_requested = false;
m->expiration_date = (uint64_t)-1; m->expiration_date = (uint64_t)-1;
end_error: end_error:
DEBUG_ARG("m = %p", m); DEBUG_ARG("m = %p", m);

View File

@ -79,7 +79,7 @@ struct mbuf {
int m_len; /* Amount of data in this mbuf */ int m_len; /* Amount of data in this mbuf */
Slirp *slirp; Slirp *slirp;
bool arp_requested; bool resolution_requested;
uint64_t expiration_date; uint64_t expiration_date;
/* start of dynamic buffer area, must be last element */ /* start of dynamic buffer area, must be last element */
union { union {

View File

@ -23,6 +23,7 @@
*/ */
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/error-report.h"
#include "sysemu/char.h" #include "sysemu/char.h"
#include "slirp.h" #include "slirp.h"
#include "hw/hw.h" #include "hw/hw.h"
@ -234,7 +235,7 @@ Slirp *slirp_init(int restricted, struct in_addr vnetwork,
slirp->opaque = opaque; slirp->opaque = opaque;
register_savevm(NULL, "slirp", 0, 3, register_savevm(NULL, "slirp", 0, 4,
slirp_state_save, slirp_state_load, slirp); slirp_state_save, slirp_state_load, slirp);
QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry);
@ -762,20 +763,15 @@ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
} }
} }
/* Output the IP packet to the ethernet device. Returns 0 if the packet must be /* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no
* re-queued. * packet should be sent, 0 if the packet must be re-queued, 2 if the packet
* is ready to go.
*/ */
int if_encap(Slirp *slirp, struct mbuf *ifm) static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,
uint8_t ethaddr[ETH_ALEN])
{ {
uint8_t buf[1600];
struct ethhdr *eh = (struct ethhdr *)buf;
uint8_t ethaddr[ETH_ALEN];
const struct ip *iph = (const struct ip *)ifm->m_data; const struct ip *iph = (const struct ip *)ifm->m_data;
if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
return 1;
}
if (iph->ip_dst.s_addr == 0) { if (iph->ip_dst.s_addr == 0) {
/* 0.0.0.0 can not be a destination address, something went wrong, /* 0.0.0.0 can not be a destination address, something went wrong,
* avoid making it worse */ * avoid making it worse */
@ -786,7 +782,7 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
struct ethhdr *reh = (struct ethhdr *)arp_req; struct ethhdr *reh = (struct ethhdr *)arp_req;
struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN); struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
if (!ifm->arp_requested) { if (!ifm->resolution_requested) {
/* If the client addr is not known, send an ARP request */ /* If the client addr is not known, send an ARP request */
memset(reh->h_dest, 0xff, ETH_ALEN); memset(reh->h_dest, 0xff, ETH_ALEN);
memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
@ -812,22 +808,62 @@ int if_encap(Slirp *slirp, struct mbuf *ifm)
rah->ar_tip = iph->ip_dst.s_addr; rah->ar_tip = iph->ip_dst.s_addr;
slirp->client_ipaddr = iph->ip_dst; slirp->client_ipaddr = iph->ip_dst;
slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
ifm->arp_requested = true; ifm->resolution_requested = true;
/* Expire request and drop outgoing packet after 1 second */ /* Expire request and drop outgoing packet after 1 second */
ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL; ifm->expiration_date = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + 1000000000ULL;
} }
return 0; return 0;
} else { } else {
memcpy(eh->h_dest, ethaddr, ETH_ALEN);
memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4); memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
/* XXX: not correct */ /* XXX: not correct */
memcpy(&eh->h_source[2], &slirp->vhost_addr, 4); memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
eh->h_proto = htons(ETH_P_IP); eh->h_proto = htons(ETH_P_IP);
memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN); /* Send this */
return 2;
}
}
/* Output the IP packet to the ethernet device. Returns 0 if the packet must be
* re-queued.
*/
int if_encap(Slirp *slirp, struct mbuf *ifm)
{
uint8_t buf[1600];
struct ethhdr *eh = (struct ethhdr *)buf;
uint8_t ethaddr[ETH_ALEN];
const struct ip *iph = (const struct ip *)ifm->m_data;
int ret;
if (ifm->m_len + ETH_HLEN > sizeof(buf)) {
return 1; return 1;
} }
switch (iph->ip_v) {
case IPVERSION:
ret = if_encap4(slirp, ifm, eh, ethaddr);
if (ret < 2) {
return ret;
}
break;
default:
/* Do not assert while we don't manage IP6VERSION */
/* assert(0); */
break;
}
memcpy(eh->h_dest, ethaddr, ETH_ALEN);
DEBUG_ARGS((dfd, " src = %02x:%02x:%02x:%02x:%02x:%02x\n",
eh->h_source[0], eh->h_source[1], eh->h_source[2],
eh->h_source[3], eh->h_source[4], eh->h_source[5]));
DEBUG_ARGS((dfd, " dst = %02x:%02x:%02x:%02x:%02x:%02x\n",
eh->h_dest[0], eh->h_dest[1], eh->h_dest[2],
eh->h_dest[3], eh->h_dest[4], eh->h_dest[5]));
memcpy(buf + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);
slirp_output(slirp->opaque, buf, ifm->m_len + ETH_HLEN);
return 1;
} }
/* Drop host forwarding rule, return 0 if found. */ /* Drop host forwarding rule, return 0 if found. */
@ -1011,10 +1047,26 @@ static void slirp_sbuf_save(QEMUFile *f, struct sbuf *sbuf)
static void slirp_socket_save(QEMUFile *f, struct socket *so) static void slirp_socket_save(QEMUFile *f, struct socket *so)
{ {
qemu_put_be32(f, so->so_urgc); qemu_put_be32(f, so->so_urgc);
qemu_put_be32(f, so->so_faddr.s_addr); qemu_put_be16(f, so->so_ffamily);
qemu_put_be32(f, so->so_laddr.s_addr); switch (so->so_ffamily) {
qemu_put_be16(f, so->so_fport); case AF_INET:
qemu_put_be16(f, so->so_lport); qemu_put_be32(f, so->so_faddr.s_addr);
qemu_put_be16(f, so->so_fport);
break;
default:
error_report(
"so_ffamily unknown, unable to save so_faddr and so_fport\n");
}
qemu_put_be16(f, so->so_lfamily);
switch (so->so_lfamily) {
case AF_INET:
qemu_put_be32(f, so->so_laddr.s_addr);
qemu_put_be16(f, so->so_lport);
break;
default:
error_report(
"so_ffamily unknown, unable to save so_laddr and so_lport\n");
}
qemu_put_byte(f, so->so_iptos); qemu_put_byte(f, so->so_iptos);
qemu_put_byte(f, so->so_emu); qemu_put_byte(f, so->so_emu);
qemu_put_byte(f, so->so_type); qemu_put_byte(f, so->so_type);
@ -1134,10 +1186,26 @@ static int slirp_socket_load(QEMUFile *f, struct socket *so)
return -ENOMEM; return -ENOMEM;
so->so_urgc = qemu_get_be32(f); so->so_urgc = qemu_get_be32(f);
so->so_faddr.s_addr = qemu_get_be32(f); so->so_ffamily = qemu_get_be16(f);
so->so_laddr.s_addr = qemu_get_be32(f); switch (so->so_ffamily) {
so->so_fport = qemu_get_be16(f); case AF_INET:
so->so_lport = qemu_get_be16(f); so->so_faddr.s_addr = qemu_get_be32(f);
so->so_fport = qemu_get_be16(f);
break;
default:
error_report(
"so_ffamily unknown, unable to restore so_faddr and so_lport\n");
}
so->so_lfamily = qemu_get_be16(f);
switch (so->so_lfamily) {
case AF_INET:
so->so_laddr.s_addr = qemu_get_be32(f);
so->so_lport = qemu_get_be16(f);
break;
default:
error_report(
"so_ffamily unknown, unable to restore so_laddr and so_lport\n");
}
so->so_iptos = qemu_get_byte(f); so->so_iptos = qemu_get_byte(f);
so->so_emu = qemu_get_byte(f); so->so_emu = qemu_get_byte(f);
so->so_type = qemu_get_byte(f); so->so_type = qemu_get_byte(f);

View File

@ -327,7 +327,7 @@ void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbu
struct tcpcb * tcp_newtcpcb(struct socket *); struct tcpcb * tcp_newtcpcb(struct socket *);
struct tcpcb * tcp_close(register struct tcpcb *); struct tcpcb * tcp_close(register struct tcpcb *);
void tcp_sockclosed(struct tcpcb *); void tcp_sockclosed(struct tcpcb *);
int tcp_fconnect(struct socket *); int tcp_fconnect(struct socket *, unsigned short af);
void tcp_connect(struct socket *); void tcp_connect(struct socket *);
int tcp_attach(struct socket *); int tcp_attach(struct socket *);
uint8_t tcp_tos(struct socket *); uint8_t tcp_tos(struct socket *);

View File

@ -15,24 +15,26 @@
static void sofcantrcvmore(struct socket *so); static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so); static void sofcantsendmore(struct socket *so);
struct socket * struct socket *solookup(struct socket **last, struct socket *head,
solookup(struct socket *head, struct in_addr laddr, u_int lport, struct sockaddr_storage *lhost, struct sockaddr_storage *fhost)
struct in_addr faddr, u_int fport)
{ {
struct socket *so; struct socket *so = *last;
for (so = head->so_next; so != head; so = so->so_next) { /* Optimisation */
if (so->so_lport == lport && if (so != head && sockaddr_equal(&(so->lhost.ss), lhost)
so->so_laddr.s_addr == laddr.s_addr && && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
so->so_faddr.s_addr == faddr.s_addr && return so;
so->so_fport == fport) }
break;
}
if (so == head) for (so = head->so_next; so != head; so = so->so_next) {
return (struct socket *)NULL; if (sockaddr_equal(&(so->lhost.ss), lhost)
return so; && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
*last = so;
return so;
}
}
return (struct socket *)NULL;
} }
/* /*
@ -437,8 +439,9 @@ sowrite(struct socket *so)
void void
sorecvfrom(struct socket *so) sorecvfrom(struct socket *so)
{ {
struct sockaddr_in addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_in); struct sockaddr_storage saddr, daddr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
DEBUG_CALL("sorecvfrom"); DEBUG_CALL("sorecvfrom");
DEBUG_ARG("so = %p", so); DEBUG_ARG("so = %p", so);
@ -525,9 +528,21 @@ sorecvfrom(struct socket *so)
/* /*
* If this packet was destined for CTL_ADDR, * If this packet was destined for CTL_ADDR,
* make it look like that's where it came from, done by udp_output * make it look like that's where it came from
*/ */
udp_output(so, m, &addr); saddr = addr;
sotranslate_in(so, &saddr);
daddr = so->lhost.ss;
switch (so->so_ffamily) {
case AF_INET:
udp_output(so, m, (struct sockaddr_in *) &saddr,
(struct sockaddr_in *) &daddr,
so->so_iptos);
break;
default:
break;
}
} /* rx error */ } /* rx error */
} /* if ping packet */ } /* if ping packet */
} }
@ -538,33 +553,20 @@ sorecvfrom(struct socket *so)
int int
sosendto(struct socket *so, struct mbuf *m) sosendto(struct socket *so, struct mbuf *m)
{ {
Slirp *slirp = so->slirp;
int ret; int ret;
struct sockaddr_in addr; struct sockaddr_storage addr;
DEBUG_CALL("sosendto"); DEBUG_CALL("sosendto");
DEBUG_ARG("so = %p", so); DEBUG_ARG("so = %p", so);
DEBUG_ARG("m = %p", m); DEBUG_ARG("m = %p", m);
addr.sin_family = AF_INET; addr = so->fhost.ss;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == DEBUG_CALL(" sendto()ing)");
slirp->vnetwork_addr.s_addr) { sotranslate_out(so, &addr);
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else
addr.sin_addr = so->so_faddr;
addr.sin_port = so->so_fport;
DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
/* Don't care what port we get */ /* Don't care what port we get */
ret = sendto(so->s, m->m_data, m->m_len, 0, ret = sendto(so->s, m->m_data, m->m_len, 0,
(struct sockaddr *)&addr, sizeof (struct sockaddr)); (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) if (ret < 0)
return -1; return -1;
@ -619,6 +621,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
so->so_state &= SS_PERSISTENT_MASK; so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= (SS_FACCEPTCONN | flags); so->so_state |= (SS_FACCEPTCONN | flags);
so->so_lfamily = AF_INET;
so->so_lport = lport; /* Kept in network format */ so->so_lport = lport; /* Kept in network format */
so->so_laddr.s_addr = laddr; /* Ditto */ so->so_laddr.s_addr = laddr; /* Ditto */
@ -645,6 +648,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
getsockname(s,(struct sockaddr *)&addr,&addrlen); getsockname(s,(struct sockaddr *)&addr,&addrlen);
so->so_ffamily = AF_INET;
so->so_fport = addr.sin_port; so->so_fport = addr.sin_port;
if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr) if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
so->so_faddr = slirp->vhost_addr; so->so_faddr = slirp->vhost_addr;
@ -718,3 +722,81 @@ sofwdrain(struct socket *so)
else else
sofcantsendmore(so); sofcantsendmore(so);
} }
/*
* Translate addr in host addr when it is a virtual address
*/
void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
switch (addr->ss_family) {
case AF_INET:
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&sin->sin_addr) < 0) {
sin->sin_addr = loopback_addr;
}
} else {
sin->sin_addr = loopback_addr;
}
}
DEBUG_MISC((dfd, " addr.sin_port=%d, "
"addr.sin_addr.s_addr=%.16s\n",
ntohs(sin->sin_port), inet_ntoa(sin->sin_addr)));
break;
default:
break;
}
}
void sotranslate_in(struct socket *so, struct sockaddr_storage *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in *sin = (struct sockaddr_in *)addr;
switch (addr->ss_family) {
case AF_INET:
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
sin->sin_addr = slirp->vhost_addr;
} else if (sin->sin_addr.s_addr == loopback_addr.s_addr ||
so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
sin->sin_addr = so->so_faddr;
}
}
break;
default:
break;
}
}
/*
* Translate connections from localhost to the real hostname
*/
void sotranslate_accept(struct socket *so)
{
Slirp *slirp = so->slirp;
switch (so->so_ffamily) {
case AF_INET:
if (so->so_faddr.s_addr == INADDR_ANY ||
(so->so_faddr.s_addr & loopback_mask) ==
(loopback_addr.s_addr & loopback_mask)) {
so->so_faddr = slirp->vhost_addr;
}
break;
default:
break;
}
}

View File

@ -31,10 +31,21 @@ struct socket {
struct tcpiphdr *so_ti; /* Pointer to the original ti within struct tcpiphdr *so_ti; /* Pointer to the original ti within
* so_mconn, for non-blocking connections */ * so_mconn, for non-blocking connections */
int so_urgc; int so_urgc;
struct in_addr so_faddr; /* foreign host table entry */ union { /* foreign host */
struct in_addr so_laddr; /* local host table entry */ struct sockaddr_storage ss;
uint16_t so_fport; /* foreign port */ struct sockaddr_in sin;
uint16_t so_lport; /* local port */ } fhost;
#define so_faddr fhost.sin.sin_addr
#define so_fport fhost.sin.sin_port
#define so_ffamily fhost.ss.ss_family
union { /* local host */
struct sockaddr_storage ss;
struct sockaddr_in sin;
} lhost;
#define so_laddr lhost.sin.sin_addr
#define so_lport lhost.sin.sin_port
#define so_lfamily lhost.ss.ss_family
uint8_t so_iptos; /* Type of service */ uint8_t so_iptos; /* Type of service */
uint8_t so_emu; /* Is the socket emulated? */ uint8_t so_emu; /* Is the socket emulated? */
@ -76,8 +87,31 @@ struct socket {
#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */ #define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */ #define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int); static inline int sockaddr_equal(struct sockaddr_storage *a,
struct socket * socreate(Slirp *); struct sockaddr_storage *b)
{
if (a->ss_family != b->ss_family) {
return 0;
}
switch (a->ss_family) {
case AF_INET:
{
struct sockaddr_in *a4 = (struct sockaddr_in *) a;
struct sockaddr_in *b4 = (struct sockaddr_in *) b;
return a4->sin_addr.s_addr == b4->sin_addr.s_addr
&& a4->sin_port == b4->sin_port;
}
default:
g_assert_not_reached();
}
return 0;
}
struct socket *solookup(struct socket **, struct socket *,
struct sockaddr_storage *, struct sockaddr_storage *);
struct socket *socreate(Slirp *);
void sofree(struct socket *); void sofree(struct socket *);
int soread(struct socket *); int soread(struct socket *);
void sorecvoob(struct socket *); void sorecvoob(struct socket *);
@ -94,4 +128,9 @@ struct iovec; /* For win32 */
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np); size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
int soreadbuf(struct socket *so, const char *buf, int size); int soreadbuf(struct socket *so, const char *buf, int size);
void sotranslate_out(struct socket *, struct sockaddr_storage *);
void sotranslate_in(struct socket *, struct sockaddr_storage *);
void sotranslate_accept(struct socket *);
#endif /* _SOCKET_H_ */ #endif /* _SOCKET_H_ */

View File

@ -227,6 +227,8 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
int iss = 0; int iss = 0;
u_long tiwin; u_long tiwin;
int ret; int ret;
struct sockaddr_storage lhost, fhost;
struct sockaddr_in *lhost4, *fhost4;
struct ex_list *ex_ptr; struct ex_list *ex_ptr;
Slirp *slirp; Slirp *slirp;
@ -320,16 +322,16 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso)
* Locate pcb for segment. * Locate pcb for segment.
*/ */
findso: findso:
so = slirp->tcp_last_so; lhost.ss_family = AF_INET;
if (so->so_fport != ti->ti_dport || lhost4 = (struct sockaddr_in *) &lhost;
so->so_lport != ti->ti_sport || lhost4->sin_addr = ti->ti_src;
so->so_laddr.s_addr != ti->ti_src.s_addr || lhost4->sin_port = ti->ti_sport;
so->so_faddr.s_addr != ti->ti_dst.s_addr) { fhost.ss_family = AF_INET;
so = solookup(&slirp->tcb, ti->ti_src, ti->ti_sport, fhost4 = (struct sockaddr_in *) &fhost;
ti->ti_dst, ti->ti_dport); fhost4->sin_addr = ti->ti_dst;
if (so) fhost4->sin_port = ti->ti_dport;
slirp->tcp_last_so = so;
} so = solookup(&slirp->tcp_last_so, &slirp->tcb, &lhost, &fhost);
/* /*
* If the state is CLOSED (i.e., TCB does not exist) then * If the state is CLOSED (i.e., TCB does not exist) then
@ -374,10 +376,8 @@ findso:
sbreserve(&so->so_snd, TCP_SNDSPACE); sbreserve(&so->so_snd, TCP_SNDSPACE);
sbreserve(&so->so_rcv, TCP_RCVSPACE); sbreserve(&so->so_rcv, TCP_RCVSPACE);
so->so_laddr = ti->ti_src; so->lhost.ss = lhost;
so->so_lport = ti->ti_sport; so->fhost.ss = fhost;
so->so_faddr = ti->ti_dst;
so->so_fport = ti->ti_dport;
if ((so->so_iptos = tcp_tos(so)) == 0) if ((so->so_iptos = tcp_tos(so)) == 0)
so->so_iptos = ((struct ip *)ti)->ip_tos; so->so_iptos = ((struct ip *)ti)->ip_tos;
@ -584,7 +584,7 @@ findso:
goto cont_input; goto cont_input;
} }
if ((tcp_fconnect(so) == -1) && if ((tcp_fconnect(so, so->so_ffamily) == -1) &&
#if defined(_WIN32) #if defined(_WIN32)
socket_error() != WSAEWOULDBLOCK socket_error() != WSAEWOULDBLOCK
#else #else

View File

@ -324,40 +324,27 @@ tcp_sockclosed(struct tcpcb *tp)
* nonblocking. Connect returns after the SYN is sent, and does * nonblocking. Connect returns after the SYN is sent, and does
* not wait for ACK+SYN. * not wait for ACK+SYN.
*/ */
int tcp_fconnect(struct socket *so) int tcp_fconnect(struct socket *so, unsigned short af)
{ {
Slirp *slirp = so->slirp;
int ret=0; int ret=0;
DEBUG_CALL("tcp_fconnect"); DEBUG_CALL("tcp_fconnect");
DEBUG_ARG("so = %p", so); DEBUG_ARG("so = %p", so);
if( (ret = so->s = qemu_socket(AF_INET,SOCK_STREAM,0)) >= 0) { ret = so->s = qemu_socket(af, SOCK_STREAM, 0);
if (ret >= 0) {
int opt, s=so->s; int opt, s=so->s;
struct sockaddr_in addr; struct sockaddr_storage addr;
qemu_set_nonblock(s); qemu_set_nonblock(s);
socket_set_fast_reuse(s); socket_set_fast_reuse(s);
opt = 1; opt = 1;
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
addr.sin_family = AF_INET; addr = so->fhost.ss;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) == DEBUG_CALL(" connect()ing")
slirp->vnetwork_addr.s_addr) { sotranslate_out(so, &addr);
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else
addr.sin_addr = so->so_faddr;
addr.sin_port = so->so_fport;
DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
"addr.sin_addr.s_addr=%.16s\n",
ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
/* We don't care what port we get */ /* We don't care what port we get */
ret = connect(s,(struct sockaddr *)&addr,sizeof (addr)); ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
@ -413,6 +400,7 @@ void tcp_connect(struct socket *inso)
free(so); /* NOT sofree */ free(so); /* NOT sofree */
return; return;
} }
so->so_lfamily = AF_INET;
so->so_laddr = inso->so_laddr; so->so_laddr = inso->so_laddr;
so->so_lport = inso->so_lport; so->so_lport = inso->so_lport;
} }
@ -430,14 +418,8 @@ void tcp_connect(struct socket *inso)
qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
socket_set_nodelay(s); socket_set_nodelay(s);
so->so_fport = addr.sin_port; so->fhost.sin = addr;
so->so_faddr = addr.sin_addr; sotranslate_accept(so);
/* Translate connections from localhost to the real hostname */
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 */ /* Close the accept() socket, set right state */
if (inso->so_state & SS_FACCEPTONCE) { if (inso->so_state & SS_FACCEPTONCE) {

View File

@ -155,7 +155,7 @@ static int tftp_send_oack(struct tftp_session *spt,
m->m_len = sizeof(struct tftp_t) - 514 + n - m->m_len = sizeof(struct tftp_t) - 514 + n -
sizeof(struct ip) - sizeof(struct udphdr); sizeof(struct ip) - sizeof(struct udphdr);
udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
return 0; return 0;
} }
@ -193,7 +193,7 @@ static void tftp_send_error(struct tftp_session *spt,
m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
sizeof(struct ip) - sizeof(struct udphdr); sizeof(struct ip) - sizeof(struct udphdr);
udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
out: out:
tftp_session_terminate(spt); tftp_session_terminate(spt);
@ -243,7 +243,7 @@ static void tftp_send_next_block(struct tftp_session *spt,
m->m_len = sizeof(struct tftp_t) - (512 - nobytes) - m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
sizeof(struct ip) - sizeof(struct udphdr); sizeof(struct ip) - sizeof(struct udphdr);
udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
if (nobytes == 512) { if (nobytes == 512) {
tftp_session_update(spt); tftp_session_update(spt);

View File

@ -70,6 +70,8 @@ udp_input(register struct mbuf *m, int iphlen)
int len; int len;
struct ip save_ip; struct ip save_ip;
struct socket *so; struct socket *so;
struct sockaddr_storage lhost;
struct sockaddr_in *lhost4;
DEBUG_CALL("udp_input"); DEBUG_CALL("udp_input");
DEBUG_ARG("m = %p", m); DEBUG_ARG("m = %p", m);
@ -151,25 +153,12 @@ udp_input(register struct mbuf *m, int iphlen)
/* /*
* Locate pcb for datagram. * Locate pcb for datagram.
*/ */
so = slirp->udp_last_so; lhost.ss_family = AF_INET;
if (so == &slirp->udb || so->so_lport != uh->uh_sport || lhost4 = (struct sockaddr_in *) &lhost;
so->so_laddr.s_addr != ip->ip_src.s_addr) { lhost4->sin_addr = ip->ip_src;
struct socket *tmp; lhost4->sin_port = uh->uh_sport;
for (tmp = slirp->udb.so_next; tmp != &slirp->udb; so = solookup(&slirp->udp_last_so, &slirp->udb, &lhost, NULL);
tmp = tmp->so_next) {
if (tmp->so_lport == uh->uh_sport &&
tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
so = tmp;
break;
}
}
if (tmp == &slirp->udb) {
so = NULL;
} else {
slirp->udp_last_so = so;
}
}
if (so == NULL) { if (so == NULL) {
/* /*
@ -180,7 +169,7 @@ udp_input(register struct mbuf *m, int iphlen)
if (!so) { if (!so) {
goto bad; goto bad;
} }
if(udp_attach(so) == -1) { if (udp_attach(so, AF_INET) == -1) {
DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
errno,strerror(errno))); errno,strerror(errno)));
sofree(so); sofree(so);
@ -190,6 +179,7 @@ udp_input(register struct mbuf *m, int iphlen)
/* /*
* Setup fields * Setup fields
*/ */
so->so_lfamily = AF_INET;
so->so_laddr = ip->ip_src; so->so_laddr = ip->ip_src;
so->so_lport = uh->uh_sport; so->so_lport = uh->uh_sport;
@ -202,6 +192,7 @@ udp_input(register struct mbuf *m, int iphlen)
*/ */
} }
so->so_ffamily = AF_INET;
so->so_faddr = ip->ip_dst; /* XXX */ so->so_faddr = ip->ip_dst; /* XXX */
so->so_fport = uh->uh_dport; /* XXX */ so->so_fport = uh->uh_dport; /* XXX */
@ -218,6 +209,7 @@ udp_input(register struct mbuf *m, int iphlen)
*ip=save_ip; *ip=save_ip;
DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno)); icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
goto bad;
} }
m_free(so->so_m); /* used for ICMP if error on sorecvfrom */ m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
@ -233,7 +225,7 @@ bad:
m_free(m); m_free(m);
} }
int udp_output2(struct socket *so, struct mbuf *m, int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr, struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos) int iptos)
{ {
@ -284,35 +276,11 @@ int udp_output2(struct socket *so, struct mbuf *m,
return (error); return (error);
} }
int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in saddr, daddr;
saddr = *addr;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
saddr.sin_addr = slirp->vhost_addr;
} else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
saddr.sin_addr = so->so_faddr;
}
}
daddr.sin_addr = so->so_laddr;
daddr.sin_port = so->so_lport;
return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
}
int int
udp_attach(struct socket *so) udp_attach(struct socket *so, unsigned short af)
{ {
if((so->s = qemu_socket(AF_INET,SOCK_DGRAM,0)) != -1) { so->s = qemu_socket(af, SOCK_DGRAM, 0);
if (so->s != -1) {
so->so_expire = curtime + SO_EXPIRE; so->so_expire = curtime + SO_EXPIRE;
insque(so, &so->slirp->udb); insque(so, &so->slirp->udb);
} }
@ -375,13 +343,9 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
socket_set_fast_reuse(so->s); socket_set_fast_reuse(so->s);
getsockname(so->s,(struct sockaddr *)&addr,&addrlen); getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
so->so_fport = addr.sin_port; so->fhost.sin = addr;
if (addr.sin_addr.s_addr == 0 || sotranslate_accept(so);
addr.sin_addr.s_addr == loopback_addr.s_addr) { so->so_lfamily = AF_INET;
so->so_faddr = slirp->vhost_addr;
} else {
so->so_faddr = addr.sin_addr;
}
so->so_lport = lport; so->so_lport = lport;
so->so_laddr.s_addr = laddr; so->so_laddr.s_addr = laddr;
if (flags != SS_FACCEPTONCE) if (flags != SS_FACCEPTONCE)

View File

@ -76,12 +76,11 @@ struct mbuf;
void udp_init(Slirp *); void udp_init(Slirp *);
void udp_cleanup(Slirp *); void udp_cleanup(Slirp *);
void udp_input(register struct mbuf *, int); void udp_input(register struct mbuf *, int);
int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); int udp_attach(struct socket *, unsigned short af);
int udp_attach(struct socket *);
void udp_detach(struct socket *); void udp_detach(struct socket *);
struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int); int);
int udp_output2(struct socket *so, struct mbuf *m, int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr, struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos); int iptos);
#endif #endif

6
vl.c
View File

@ -3311,12 +3311,18 @@ int main(int argc, char **argv, char **envp)
#endif #endif
#ifdef CONFIG_SLIRP #ifdef CONFIG_SLIRP
case QEMU_OPTION_tftp: case QEMU_OPTION_tftp:
error_report("The -tftp option is deprecated. "
"Please use '-netdev user,tftp=...' instead.");
legacy_tftp_prefix = optarg; legacy_tftp_prefix = optarg;
break; break;
case QEMU_OPTION_bootp: case QEMU_OPTION_bootp:
error_report("The -bootp option is deprecated. "
"Please use '-netdev user,bootfile=...' instead.");
legacy_bootp_filename = optarg; legacy_bootp_filename = optarg;
break; break;
case QEMU_OPTION_redir: case QEMU_OPTION_redir:
error_report("The -redir option is deprecated. "
"Please use '-netdev user,hostfwd=...' instead.");
if (net_slirp_redir(optarg) < 0) if (net_slirp_redir(optarg) < 0)
exit(1); exit(1);
break; break;