2008-11-01 03:10:00 +08:00
|
|
|
/*
|
|
|
|
* QEMU System Emulator
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* 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 AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <zlib.h>
|
|
|
|
|
2009-07-27 22:12:56 +08:00
|
|
|
/* Needed early for CONFIG_BSD etc. */
|
2009-03-08 00:52:02 +08:00
|
|
|
#include "config-host.h"
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifndef _WIN32
|
|
|
|
#include <sys/times.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/ioctl.h>
|
2008-11-08 00:55:48 +08:00
|
|
|
#include <sys/resource.h>
|
2008-11-01 03:10:00 +08:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in.h>
|
2008-11-08 00:55:48 +08:00
|
|
|
#include <net/if.h>
|
|
|
|
#ifdef __NetBSD__
|
|
|
|
#include <net/if_tap.h>
|
|
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
#endif
|
|
|
|
#include <arpa/inet.h>
|
2008-11-01 03:10:00 +08:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/select.h>
|
2009-07-27 22:12:56 +08:00
|
|
|
#ifdef CONFIG_BSD
|
2008-11-01 03:10:00 +08:00
|
|
|
#include <sys/stat.h>
|
2009-03-08 04:06:23 +08:00
|
|
|
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
2008-11-01 03:10:00 +08:00
|
|
|
#include <libutil.h>
|
2008-11-08 00:55:48 +08:00
|
|
|
#else
|
|
|
|
#include <util.h>
|
2008-11-01 03:10:00 +08:00
|
|
|
#endif
|
|
|
|
#elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
|
|
|
|
#include <freebsd/stdlib.h>
|
|
|
|
#else
|
|
|
|
#ifdef __linux__
|
|
|
|
#include <pty.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <linux/rtc.h>
|
|
|
|
|
|
|
|
/* For the benefit of older linux systems which don't supply it,
|
|
|
|
we use a local copy of hpet.h. */
|
|
|
|
/* #include <linux/hpet.h> */
|
|
|
|
#include "hpet.h"
|
|
|
|
|
|
|
|
#include <linux/ppdev.h>
|
|
|
|
#include <linux/parport.h>
|
|
|
|
#endif
|
|
|
|
#ifdef __sun__
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ethernet.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <netinet/arp.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/ip_icmp.h> // must come after ip.h
|
|
|
|
#include <netinet/udp.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <stropts.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__OpenBSD__)
|
|
|
|
#include <util.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(CONFIG_VDE)
|
|
|
|
#include <libvdeplug.h>
|
|
|
|
#endif
|
|
|
|
|
2009-03-07 23:32:56 +08:00
|
|
|
#include "qemu-common.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "monitor.h"
|
|
|
|
#include "sysemu.h"
|
|
|
|
#include "qemu-timer.h"
|
|
|
|
#include "qemu-char.h"
|
|
|
|
#include "audio/audio.h"
|
|
|
|
#include "qemu_socket.h"
|
2009-04-22 03:56:28 +08:00
|
|
|
#include "qemu-log.h"
|
2009-10-06 19:17:06 +08:00
|
|
|
#include "qemu-config.h"
|
2009-03-07 23:32:56 +08:00
|
|
|
|
2009-06-24 20:42:30 +08:00
|
|
|
#include "slirp/libslirp.h"
|
2009-09-12 15:36:22 +08:00
|
|
|
#include "qemu-queue.h"
|
2009-03-07 23:32:56 +08:00
|
|
|
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
static VLANState *first_vlan;
|
|
|
|
|
|
|
|
/***********************************************************/
|
|
|
|
/* network device redirectors */
|
|
|
|
|
|
|
|
#if defined(DEBUG_NET) || defined(DEBUG_SLIRP)
|
|
|
|
static void hex_dump(FILE *f, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
int len, i, j, c;
|
|
|
|
|
|
|
|
for(i=0;i<size;i+=16) {
|
|
|
|
len = size - i;
|
|
|
|
if (len > 16)
|
|
|
|
len = 16;
|
|
|
|
fprintf(f, "%08x ", i);
|
|
|
|
for(j=0;j<16;j++) {
|
|
|
|
if (j < len)
|
|
|
|
fprintf(f, " %02x", buf[i+j]);
|
|
|
|
else
|
|
|
|
fprintf(f, " ");
|
|
|
|
}
|
|
|
|
fprintf(f, " ");
|
|
|
|
for(j=0;j<len;j++) {
|
|
|
|
c = buf[i+j];
|
|
|
|
if (c < ' ' || c > '~')
|
|
|
|
c = '.';
|
|
|
|
fprintf(f, "%c", c);
|
|
|
|
}
|
|
|
|
fprintf(f, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int parse_macaddr(uint8_t *macaddr, const char *p)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *last_char;
|
|
|
|
long int offset;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
offset = strtol(p, &last_char, 0);
|
|
|
|
if (0 == errno && '\0' == *last_char &&
|
|
|
|
offset >= 0 && offset <= 0xFFFFFF) {
|
|
|
|
macaddr[3] = (offset & 0xFF0000) >> 16;
|
|
|
|
macaddr[4] = (offset & 0xFF00) >> 8;
|
|
|
|
macaddr[5] = offset & 0xFF;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
for(i = 0; i < 6; i++) {
|
|
|
|
macaddr[i] = strtol(p, (char **)&p, 16);
|
|
|
|
if (i == 5) {
|
|
|
|
if (*p != '\0')
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if (*p != ':' && *p != '-')
|
|
|
|
return -1;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
|
|
|
|
{
|
|
|
|
const char *p, *p1;
|
|
|
|
int len;
|
|
|
|
p = *pp;
|
|
|
|
p1 = strchr(p, sep);
|
|
|
|
if (!p1)
|
|
|
|
return -1;
|
|
|
|
len = p1 - p;
|
|
|
|
p1++;
|
|
|
|
if (buf_size > 0) {
|
|
|
|
if (len > buf_size - 1)
|
|
|
|
len = buf_size - 1;
|
|
|
|
memcpy(buf, p, len);
|
|
|
|
buf[len] = '\0';
|
|
|
|
}
|
|
|
|
*pp = p1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_host_src_port(struct sockaddr_in *haddr,
|
|
|
|
struct sockaddr_in *saddr,
|
|
|
|
const char *input_str)
|
|
|
|
{
|
|
|
|
char *str = strdup(input_str);
|
|
|
|
char *host_str = str;
|
|
|
|
char *src_str;
|
|
|
|
const char *src_str2;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Chop off any extra arguments at the end of the string which
|
|
|
|
* would start with a comma, then fill in the src port information
|
|
|
|
* if it was provided else use the "any address" and "any port".
|
|
|
|
*/
|
|
|
|
if ((ptr = strchr(str,',')))
|
|
|
|
*ptr = '\0';
|
|
|
|
|
|
|
|
if ((src_str = strchr(input_str,'@'))) {
|
|
|
|
*src_str = '\0';
|
|
|
|
src_str++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parse_host_port(haddr, host_str) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
src_str2 = src_str;
|
|
|
|
if (!src_str || *src_str == '\0')
|
|
|
|
src_str2 = ":0";
|
|
|
|
|
|
|
|
if (parse_host_port(saddr, src_str2) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
free(str);
|
|
|
|
return(0);
|
|
|
|
|
|
|
|
fail:
|
|
|
|
free(str);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_host_port(struct sockaddr_in *saddr, const char *str)
|
|
|
|
{
|
|
|
|
char buf[512];
|
|
|
|
struct hostent *he;
|
|
|
|
const char *p, *r;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
p = str;
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
|
|
|
|
return -1;
|
|
|
|
saddr->sin_family = AF_INET;
|
|
|
|
if (buf[0] == '\0') {
|
|
|
|
saddr->sin_addr.s_addr = 0;
|
|
|
|
} else {
|
2008-11-16 21:53:32 +08:00
|
|
|
if (qemu_isdigit(buf[0])) {
|
2008-11-01 03:10:00 +08:00
|
|
|
if (!inet_aton(buf, &saddr->sin_addr))
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if ((he = gethostbyname(buf)) == NULL)
|
|
|
|
return - 1;
|
|
|
|
saddr->sin_addr = *(struct in_addr *)he->h_addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port = strtol(p, (char **)&r, 0);
|
|
|
|
if (r == p)
|
|
|
|
return -1;
|
|
|
|
saddr->sin_port = htons(port);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:46:21 +08:00
|
|
|
void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6])
|
|
|
|
{
|
|
|
|
snprintf(vc->info_str, sizeof(vc->info_str),
|
2009-01-09 03:01:37 +08:00
|
|
|
"model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
|
vc->model,
|
2009-01-08 01:46:21 +08:00
|
|
|
macaddr[0], macaddr[1], macaddr[2],
|
|
|
|
macaddr[3], macaddr[4], macaddr[5]);
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:43:44 +08:00
|
|
|
static char *assign_name(VLANClientState *vc1, const char *model)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
char buf[256];
|
|
|
|
int id = 0;
|
|
|
|
|
|
|
|
for (vlan = first_vlan; vlan; vlan = vlan->next) {
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
for (vc = vlan->first_client; vc; vc = vc->next)
|
|
|
|
if (vc != vc1 && strcmp(vc->model, model) == 0)
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "%s.%d", model, id);
|
|
|
|
|
2009-10-06 19:16:55 +08:00
|
|
|
return qemu_strdup(buf);
|
2009-01-08 01:43:44 +08:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
VLANClientState *qemu_new_vlan_client(VLANState *vlan,
|
2009-01-08 01:42:25 +08:00
|
|
|
const char *model,
|
2009-01-08 01:48:51 +08:00
|
|
|
const char *name,
|
2009-05-18 20:13:16 +08:00
|
|
|
NetCanReceive *can_receive,
|
|
|
|
NetReceive *receive,
|
|
|
|
NetReceiveIOV *receive_iov,
|
2009-04-18 01:11:08 +08:00
|
|
|
NetCleanup *cleanup,
|
2008-11-01 03:10:00 +08:00
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState *vc, **pvc;
|
|
|
|
vc = qemu_mallocz(sizeof(VLANClientState));
|
2009-10-06 19:16:55 +08:00
|
|
|
vc->model = qemu_strdup(model);
|
2009-01-08 01:48:51 +08:00
|
|
|
if (name)
|
2009-10-06 19:16:55 +08:00
|
|
|
vc->name = qemu_strdup(name);
|
2009-01-08 01:48:51 +08:00
|
|
|
else
|
|
|
|
vc->name = assign_name(vc, model);
|
2009-05-18 20:13:16 +08:00
|
|
|
vc->can_receive = can_receive;
|
|
|
|
vc->receive = receive;
|
|
|
|
vc->receive_iov = receive_iov;
|
2009-04-18 01:11:08 +08:00
|
|
|
vc->cleanup = cleanup;
|
2008-11-01 03:10:00 +08:00
|
|
|
vc->opaque = opaque;
|
|
|
|
vc->vlan = vlan;
|
|
|
|
|
|
|
|
vc->next = NULL;
|
|
|
|
pvc = &vlan->first_client;
|
|
|
|
while (*pvc != NULL)
|
|
|
|
pvc = &(*pvc)->next;
|
|
|
|
*pvc = vc;
|
|
|
|
return vc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_del_vlan_client(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
VLANClientState **pvc = &vc->vlan->first_client;
|
|
|
|
|
|
|
|
while (*pvc != NULL)
|
|
|
|
if (*pvc == vc) {
|
|
|
|
*pvc = vc->next;
|
2009-04-18 01:11:08 +08:00
|
|
|
if (vc->cleanup) {
|
|
|
|
vc->cleanup(vc);
|
|
|
|
}
|
2009-10-06 19:16:55 +08:00
|
|
|
qemu_free(vc->name);
|
|
|
|
qemu_free(vc->model);
|
2009-04-18 01:11:12 +08:00
|
|
|
qemu_free(vc);
|
2008-11-01 03:10:00 +08:00
|
|
|
break;
|
|
|
|
} else
|
|
|
|
pvc = &(*pvc)->next;
|
|
|
|
}
|
|
|
|
|
2009-02-11 23:20:51 +08:00
|
|
|
VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState **pvc = &vlan->first_client;
|
|
|
|
|
|
|
|
while (*pvc != NULL)
|
|
|
|
if ((*pvc)->opaque == opaque)
|
|
|
|
return *pvc;
|
|
|
|
else
|
|
|
|
pvc = &(*pvc)->next;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
static VLANClientState *
|
|
|
|
qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
|
|
|
|
const char *client_str)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
vlan = qemu_find_vlan(vlan_id, 0);
|
|
|
|
if (!vlan) {
|
|
|
|
monitor_printf(mon, "unknown VLAN %d\n", vlan_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (vc = vlan->first_client; vc != NULL; vc = vc->next) {
|
|
|
|
if (!strcmp(vc->name, client_str)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!vc) {
|
|
|
|
monitor_printf(mon, "can't find device %s on VLAN %d\n",
|
|
|
|
client_str, vlan_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return vc;
|
|
|
|
}
|
|
|
|
|
2009-04-29 16:36:43 +08:00
|
|
|
int qemu_can_send_packet(VLANClientState *sender)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-04-29 16:36:43 +08:00
|
|
|
VLANState *vlan = sender->vlan;
|
2008-11-01 03:10:00 +08:00
|
|
|
VLANClientState *vc;
|
|
|
|
|
2009-04-29 16:36:43 +08:00
|
|
|
for (vc = vlan->first_client; vc != NULL; vc = vc->next) {
|
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-05-18 20:13:16 +08:00
|
|
|
/* no can_receive() handler, they can always receive */
|
2009-05-18 20:33:03 +08:00
|
|
|
if (!vc->can_receive || vc->can_receive(vc)) {
|
2009-04-29 16:36:43 +08:00
|
|
|
return 1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-04-29 18:34:52 +08:00
|
|
|
static int
|
2009-04-22 03:56:41 +08:00
|
|
|
qemu_deliver_packet(VLANClientState *sender, const uint8_t *buf, int size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
2009-04-29 18:34:52 +08:00
|
|
|
int ret = -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
sender->vlan->delivering = 1;
|
|
|
|
|
2009-04-22 03:56:41 +08:00
|
|
|
for (vc = sender->vlan->first_client; vc != NULL; vc = vc->next) {
|
2009-04-29 18:34:52 +08:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
2009-04-22 03:56:41 +08:00
|
|
|
}
|
2009-04-29 18:34:52 +08:00
|
|
|
|
|
|
|
if (vc->link_down) {
|
|
|
|
ret = size;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = vc->receive(vc, buf, size);
|
|
|
|
|
|
|
|
ret = (ret >= 0) ? ret : len;
|
2009-04-22 03:56:41 +08:00
|
|
|
}
|
2009-04-29 18:34:52 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
sender->vlan->delivering = 0;
|
|
|
|
|
2009-04-29 18:34:52 +08:00
|
|
|
return ret;
|
2009-04-22 03:56:41 +08:00
|
|
|
}
|
|
|
|
|
2009-06-19 01:21:29 +08:00
|
|
|
void qemu_purge_queued_packets(VLANClientState *vc)
|
|
|
|
{
|
2009-08-26 18:47:04 +08:00
|
|
|
VLANPacket *packet, *next;
|
2009-06-19 01:21:29 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_FOREACH_SAFE(packet, &vc->vlan->send_queue, entry, next) {
|
2009-06-19 01:21:29 +08:00
|
|
|
if (packet->sender == vc) {
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_REMOVE(&vc->vlan->send_queue, packet, entry);
|
2009-06-19 01:21:29 +08:00
|
|
|
qemu_free(packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
void qemu_flush_queued_packets(VLANClientState *vc)
|
2009-04-29 18:48:12 +08:00
|
|
|
{
|
2009-09-12 15:36:22 +08:00
|
|
|
while (!QTAILQ_EMPTY(&vc->vlan->send_queue)) {
|
2009-08-26 18:47:04 +08:00
|
|
|
VLANPacket *packet;
|
2009-04-29 19:15:26 +08:00
|
|
|
int ret;
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
packet = QTAILQ_FIRST(&vc->vlan->send_queue);
|
|
|
|
QTAILQ_REMOVE(&vc->vlan->send_queue, packet, entry);
|
2009-04-29 19:15:26 +08:00
|
|
|
|
|
|
|
ret = qemu_deliver_packet(packet->sender, packet->data, packet->size);
|
|
|
|
if (ret == 0 && packet->sent_cb != NULL) {
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_INSERT_HEAD(&vc->vlan->send_queue, packet, entry);
|
2009-04-29 19:15:26 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (packet->sent_cb)
|
2009-06-19 01:21:35 +08:00
|
|
|
packet->sent_cb(packet->sender, ret);
|
2009-04-29 19:15:26 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
qemu_free(packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
static void qemu_enqueue_packet(VLANClientState *sender,
|
|
|
|
const uint8_t *buf, int size,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-29 18:48:12 +08:00
|
|
|
{
|
|
|
|
VLANPacket *packet;
|
|
|
|
|
|
|
|
packet = qemu_malloc(sizeof(VLANPacket) + size);
|
|
|
|
packet->sender = sender;
|
|
|
|
packet->size = size;
|
2009-04-29 19:15:26 +08:00
|
|
|
packet->sent_cb = sent_cb;
|
2009-04-29 18:48:12 +08:00
|
|
|
memcpy(packet->data, buf, size);
|
2009-08-26 18:47:04 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_INSERT_TAIL(&sender->vlan->send_queue, packet, entry);
|
2009-04-29 18:48:12 +08:00
|
|
|
}
|
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
ssize_t qemu_send_packet_async(VLANClientState *sender,
|
|
|
|
const uint8_t *buf, int size,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-22 03:56:41 +08:00
|
|
|
{
|
2009-04-29 19:15:26 +08:00
|
|
|
int ret;
|
2009-04-22 03:56:41 +08:00
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
if (sender->link_down) {
|
|
|
|
return size;
|
|
|
|
}
|
2009-01-09 03:44:06 +08:00
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifdef DEBUG_NET
|
2009-04-29 19:15:26 +08:00
|
|
|
printf("vlan %d send:\n", sender->vlan->id);
|
2008-11-01 03:10:00 +08:00
|
|
|
hex_dump(stdout, buf, size);
|
|
|
|
#endif
|
2009-04-29 19:15:26 +08:00
|
|
|
|
|
|
|
if (sender->vlan->delivering) {
|
|
|
|
qemu_enqueue_packet(sender, buf, size, NULL);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qemu_deliver_packet(sender, buf, size);
|
|
|
|
if (ret == 0 && sent_cb != NULL) {
|
|
|
|
qemu_enqueue_packet(sender, buf, size, sent_cb);
|
|
|
|
return 0;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
2009-04-29 18:48:12 +08:00
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
qemu_flush_queued_packets(sender);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
qemu_send_packet_async(vc, buf, size, NULL);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2008-12-18 03:13:11 +08:00
|
|
|
static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
|
|
|
|
int iovcnt)
|
|
|
|
{
|
|
|
|
uint8_t buffer[4096];
|
|
|
|
size_t offset = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
len = MIN(sizeof(buffer) - offset, iov[i].iov_len);
|
|
|
|
memcpy(buffer + offset, iov[i].iov_base, len);
|
|
|
|
offset += len;
|
|
|
|
}
|
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
return vc->receive(vc, buffer, offset);
|
2008-12-18 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
2009-01-26 23:37:44 +08:00
|
|
|
static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt)
|
|
|
|
{
|
|
|
|
size_t offset = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < iovcnt; i++)
|
|
|
|
offset += iov[i].iov_len;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
static int qemu_deliver_packet_iov(VLANClientState *sender,
|
|
|
|
const struct iovec *iov, int iovcnt)
|
2008-12-18 03:13:11 +08:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
2009-04-29 18:48:12 +08:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
sender->vlan->delivering = 1;
|
|
|
|
|
|
|
|
for (vc = sender->vlan->first_client; vc != NULL; vc = vc->next) {
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vc->link_down) {
|
|
|
|
ret = calc_iov_length(iov, iovcnt);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vc->receive_iov) {
|
|
|
|
len = vc->receive_iov(vc, iov, iovcnt);
|
|
|
|
} else {
|
|
|
|
len = vc_sendv_compat(vc, iov, iovcnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = (ret >= 0) ? ret : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
sender->vlan->delivering = 0;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t qemu_enqueue_packet_iov(VLANClientState *sender,
|
2009-04-29 19:15:26 +08:00
|
|
|
const struct iovec *iov, int iovcnt,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-29 18:48:12 +08:00
|
|
|
{
|
2009-05-08 18:34:17 +08:00
|
|
|
VLANPacket *packet;
|
2009-04-29 18:48:12 +08:00
|
|
|
size_t max_len = 0;
|
2009-05-08 18:34:17 +08:00
|
|
|
int i;
|
2008-12-18 03:13:11 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
max_len = calc_iov_length(iov, iovcnt);
|
2009-01-26 23:37:44 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
packet = qemu_malloc(sizeof(VLANPacket) + max_len);
|
|
|
|
packet->sender = sender;
|
2009-04-29 19:15:26 +08:00
|
|
|
packet->sent_cb = sent_cb;
|
2009-04-29 18:48:12 +08:00
|
|
|
packet->size = 0;
|
2009-05-08 18:34:17 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
for (i = 0; i < iovcnt; i++) {
|
|
|
|
size_t len = iov[i].iov_len;
|
2009-05-08 18:34:17 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
memcpy(packet->data + packet->size, iov[i].iov_base, len);
|
|
|
|
packet->size += len;
|
|
|
|
}
|
2008-12-18 03:13:11 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_INSERT_TAIL(&sender->vlan->send_queue, packet, entry);
|
2008-12-18 03:13:11 +08:00
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
return packet->size;
|
|
|
|
}
|
2008-12-18 03:13:11 +08:00
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
ssize_t qemu_sendv_packet_async(VLANClientState *sender,
|
|
|
|
const struct iovec *iov, int iovcnt,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-29 18:48:12 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (sender->link_down) {
|
|
|
|
return calc_iov_length(iov, iovcnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sender->vlan->delivering) {
|
2009-04-29 19:15:26 +08:00
|
|
|
return qemu_enqueue_packet_iov(sender, iov, iovcnt, NULL);
|
2008-12-18 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
2009-04-29 18:48:12 +08:00
|
|
|
ret = qemu_deliver_packet_iov(sender, iov, iovcnt);
|
2009-04-29 19:15:26 +08:00
|
|
|
if (ret == 0 && sent_cb != NULL) {
|
|
|
|
qemu_enqueue_packet_iov(sender, iov, iovcnt, sent_cb);
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-29 18:48:12 +08:00
|
|
|
|
|
|
|
qemu_flush_queued_packets(sender);
|
|
|
|
|
|
|
|
return ret;
|
2008-12-18 03:13:11 +08:00
|
|
|
}
|
|
|
|
|
2009-04-29 19:15:26 +08:00
|
|
|
ssize_t
|
|
|
|
qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt)
|
|
|
|
{
|
|
|
|
return qemu_sendv_packet_async(vc, iov, iovcnt, NULL);
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#if defined(CONFIG_SLIRP)
|
|
|
|
|
|
|
|
/* slirp network adapter */
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
#define SLIRP_CFG_HOSTFWD 1
|
|
|
|
#define SLIRP_CFG_LEGACY 2
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-05-08 18:34:18 +08:00
|
|
|
struct slirp_config_str {
|
|
|
|
struct slirp_config_str *next;
|
2009-06-24 20:42:28 +08:00
|
|
|
int flags;
|
|
|
|
char str[1024];
|
2009-06-24 20:42:28 +08:00
|
|
|
int legacy_format;
|
2009-05-08 18:34:18 +08:00
|
|
|
};
|
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
typedef struct SlirpState {
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_ENTRY(SlirpState) entry;
|
2009-06-24 20:42:31 +08:00
|
|
|
VLANClientState *vc;
|
|
|
|
Slirp *slirp;
|
2009-06-24 20:42:31 +08:00
|
|
|
#ifndef _WIN32
|
|
|
|
char smb_dir[128];
|
|
|
|
#endif
|
2009-06-24 20:42:31 +08:00
|
|
|
} SlirpState;
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
static struct slirp_config_str *slirp_configs;
|
|
|
|
const char *legacy_tftp_prefix;
|
|
|
|
const char *legacy_bootp_filename;
|
2009-09-12 15:36:22 +08:00
|
|
|
static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(slirp_stacks);
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
int legacy_format);
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str,
|
2009-06-24 20:42:28 +08:00
|
|
|
int legacy_format);
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-06-13 16:44:31 +08:00
|
|
|
#ifndef _WIN32
|
2009-06-24 20:42:28 +08:00
|
|
|
static const char *legacy_smb_export;
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_smb(SlirpState *s, const char *exported_dir,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
struct in_addr vserver_addr);
|
2009-06-24 20:42:31 +08:00
|
|
|
static void slirp_smb_cleanup(SlirpState *s);
|
|
|
|
#else
|
|
|
|
static inline void slirp_smb_cleanup(SlirpState *s) { }
|
2009-06-13 16:44:31 +08:00
|
|
|
#endif
|
2009-05-08 18:34:18 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
int slirp_can_output(void *opaque)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s = opaque;
|
|
|
|
|
|
|
|
return qemu_can_send_packet(s->vc);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s = opaque;
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifdef DEBUG_SLIRP
|
|
|
|
printf("slirp output:\n");
|
|
|
|
hex_dump(stdout, pkt, pkt_len);
|
|
|
|
#endif
|
2009-06-24 20:42:31 +08:00
|
|
|
qemu_send_packet(s->vc, pkt, pkt_len);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t slirp_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s = vc->opaque;
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifdef DEBUG_SLIRP
|
|
|
|
printf("slirp input:\n");
|
|
|
|
hex_dump(stdout, buf, size);
|
|
|
|
#endif
|
2009-06-24 20:42:31 +08:00
|
|
|
slirp_input(s->slirp, buf, size);
|
2009-05-18 20:40:55 +08:00
|
|
|
return size;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-04-22 04:49:11 +08:00
|
|
|
static void net_slirp_cleanup(VLANClientState *vc)
|
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s = vc->opaque;
|
|
|
|
|
|
|
|
slirp_cleanup(s->slirp);
|
2009-06-24 20:42:31 +08:00
|
|
|
slirp_smb_cleanup(s);
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_REMOVE(&slirp_stacks, s, entry);
|
2009-06-24 20:42:31 +08:00
|
|
|
qemu_free(s);
|
2009-04-22 04:49:11 +08:00
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int net_slirp_init(VLANState *vlan, const char *model,
|
2009-06-24 20:42:28 +08:00
|
|
|
const char *name, int restricted,
|
|
|
|
const char *vnetwork, const char *vhost,
|
|
|
|
const char *vhostname, const char *tftp_export,
|
|
|
|
const char *bootfile, const char *vdhcp_start,
|
|
|
|
const char *vnameserver, const char *smb_export,
|
|
|
|
const char *vsmbserver)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
/* default settings according to historic slirp */
|
2009-07-11 01:43:14 +08:00
|
|
|
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
|
|
|
|
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
|
2009-06-24 20:42:31 +08:00
|
|
|
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
|
|
|
|
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
|
|
|
|
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
|
2009-06-24 20:42:28 +08:00
|
|
|
#ifndef _WIN32
|
2009-06-24 20:42:31 +08:00
|
|
|
struct in_addr smbsrv = { .s_addr = 0 };
|
2009-06-24 20:42:28 +08:00
|
|
|
#endif
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s;
|
|
|
|
char buf[20];
|
|
|
|
uint32_t addr;
|
|
|
|
int shift;
|
|
|
|
char *end;
|
2009-10-06 19:16:56 +08:00
|
|
|
struct slirp_config_str *config;
|
2009-06-24 20:42:31 +08:00
|
|
|
|
|
|
|
if (!tftp_export) {
|
|
|
|
tftp_export = legacy_tftp_prefix;
|
|
|
|
}
|
|
|
|
if (!bootfile) {
|
|
|
|
bootfile = legacy_bootp_filename;
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (vnetwork) {
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
|
|
|
|
if (!inet_aton(vnetwork, &net)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
addr = ntohl(net.s_addr);
|
|
|
|
if (!(addr & 0x80000000)) {
|
|
|
|
mask.s_addr = htonl(0xff000000); /* class A */
|
|
|
|
} else if ((addr & 0xfff00000) == 0xac100000) {
|
|
|
|
mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
|
|
|
|
} else if ((addr & 0xc0000000) == 0x80000000) {
|
|
|
|
mask.s_addr = htonl(0xffff0000); /* class B */
|
|
|
|
} else if ((addr & 0xffff0000) == 0xc0a80000) {
|
|
|
|
mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
|
|
|
|
} else if ((addr & 0xffff0000) == 0xc6120000) {
|
|
|
|
mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
|
|
|
|
} else if ((addr & 0xe0000000) == 0xe0000000) {
|
|
|
|
mask.s_addr = htonl(0xffffff00); /* class C */
|
2009-06-24 20:42:28 +08:00
|
|
|
} else {
|
2009-06-24 20:42:31 +08:00
|
|
|
mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!inet_aton(buf, &net)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
shift = strtol(vnetwork, &end, 10);
|
|
|
|
if (*end != '\0') {
|
|
|
|
if (!inet_aton(vnetwork, &mask)) {
|
2009-06-24 20:42:28 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
} else if (shift < 4 || shift > 32) {
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
mask.s_addr = htonl(0xffffffff << (32 - shift));
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
net.s_addr &= mask.s_addr;
|
|
|
|
host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
|
|
|
|
dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
|
|
|
|
dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (vhost && !inet_aton(vhost, &host)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((host.s_addr & mask.s_addr) != net.s_addr) {
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
|
|
|
|
dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (vnameserver && !inet_aton(vnameserver, &dns)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((dns.s_addr & mask.s_addr) != net.s_addr ||
|
|
|
|
dns.s_addr == host.s_addr) {
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
2009-06-24 20:42:31 +08:00
|
|
|
if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
|
|
|
|
return -1;
|
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
#endif
|
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
s = qemu_mallocz(sizeof(SlirpState));
|
|
|
|
s->slirp = slirp_init(restricted, net, mask, host, vhostname,
|
|
|
|
tftp_export, bootfile, dhcp, dns, s);
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
|
2009-05-08 18:34:18 +08:00
|
|
|
|
2009-10-06 19:16:56 +08:00
|
|
|
for (config = slirp_configs; config; config = config->next) {
|
2009-06-24 20:42:31 +08:00
|
|
|
if (config->flags & SLIRP_CFG_HOSTFWD) {
|
2009-10-06 19:16:58 +08:00
|
|
|
if (slirp_hostfwd(s, config->str,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
config->flags & SLIRP_CFG_LEGACY) < 0)
|
|
|
|
return -1;
|
2009-06-24 20:42:31 +08:00
|
|
|
} else {
|
2009-10-06 19:16:58 +08:00
|
|
|
if (slirp_guestfwd(s, config->str,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
config->flags & SLIRP_CFG_LEGACY) < 0)
|
|
|
|
return -1;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
}
|
2009-05-08 18:34:18 +08:00
|
|
|
#ifndef _WIN32
|
2009-06-24 20:42:31 +08:00
|
|
|
if (!smb_export) {
|
|
|
|
smb_export = legacy_smb_export;
|
|
|
|
}
|
|
|
|
if (smb_export) {
|
2009-10-06 19:16:58 +08:00
|
|
|
if (slirp_smb(s, smb_export, smbsrv) < 0)
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
#endif
|
2009-05-08 18:34:18 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
s->vc = qemu_new_vlan_client(vlan, model, name, NULL, slirp_receive, NULL,
|
|
|
|
net_slirp_cleanup, s);
|
2009-06-24 20:42:32 +08:00
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
|
|
|
"net=%s, restricted=%c", inet_ntoa(net), restricted ? 'y' : 'n');
|
2008-11-01 03:10:00 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:32 +08:00
|
|
|
static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
|
|
|
|
const char *stack)
|
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
if (vlan) {
|
|
|
|
vc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack);
|
|
|
|
if (!vc) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (strcmp(vc->model, "user")) {
|
|
|
|
monitor_printf(mon, "invalid device specified\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return vc->opaque;
|
|
|
|
} else {
|
2009-09-12 15:36:22 +08:00
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
2009-06-24 20:42:32 +08:00
|
|
|
monitor_printf(mon, "user mode network stack not in use\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-09-12 15:36:22 +08:00
|
|
|
return QTAILQ_FIRST(&slirp_stacks);
|
2009-06-24 20:42:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:15 +08:00
|
|
|
void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict)
|
2009-05-26 19:03:26 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr host_addr = { .s_addr = INADDR_ANY };
|
2009-05-26 19:03:26 +08:00
|
|
|
int host_port;
|
|
|
|
char buf[256] = "";
|
2009-06-24 20:42:32 +08:00
|
|
|
const char *src_str, *p;
|
|
|
|
SlirpState *s;
|
2009-05-26 19:03:26 +08:00
|
|
|
int is_udp = 0;
|
2009-06-24 20:42:29 +08:00
|
|
|
int err;
|
2009-08-29 02:27:15 +08:00
|
|
|
const char *arg1 = qdict_get_str(qdict, "arg1");
|
|
|
|
const char *arg2 = qdict_get_try_str(qdict, "arg2");
|
|
|
|
const char *arg3 = qdict_get_try_str(qdict, "arg3");
|
2009-05-26 19:03:26 +08:00
|
|
|
|
2009-06-24 20:42:32 +08:00
|
|
|
if (arg2) {
|
|
|
|
s = slirp_lookup(mon, arg1, arg2);
|
|
|
|
src_str = arg3;
|
|
|
|
} else {
|
|
|
|
s = slirp_lookup(mon, NULL, NULL);
|
|
|
|
src_str = arg1;
|
|
|
|
}
|
|
|
|
if (!s) {
|
2009-05-26 19:03:26 +08:00
|
|
|
return;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
2009-05-26 19:03:26 +08:00
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
if (!src_str || !src_str[0])
|
2009-05-26 19:03:26 +08:00
|
|
|
goto fail_syntax;
|
|
|
|
|
2009-06-24 20:42:32 +08:00
|
|
|
p = src_str;
|
2009-05-26 19:03:26 +08:00
|
|
|
get_str_sep(buf, sizeof(buf), &p, ':');
|
|
|
|
|
|
|
|
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
|
|
|
|
is_udp = 0;
|
|
|
|
} else if (!strcmp(buf, "udp")) {
|
|
|
|
is_udp = 1;
|
|
|
|
} else {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2009-05-26 19:03:26 +08:00
|
|
|
host_port = atoi(p);
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp,
|
2009-06-24 20:42:31 +08:00
|
|
|
host_addr, host_port);
|
2009-05-26 19:03:26 +08:00
|
|
|
|
2009-06-24 20:42:29 +08:00
|
|
|
monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
|
|
|
|
err ? "removed" : "not found");
|
2009-05-26 19:03:26 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail_syntax:
|
|
|
|
monitor_printf(mon, "invalid format\n");
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
int legacy_format)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr host_addr = { .s_addr = INADDR_ANY };
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr guest_addr = { .s_addr = 0 };
|
2008-11-01 03:10:00 +08:00
|
|
|
int host_port, guest_port;
|
2009-05-08 18:34:18 +08:00
|
|
|
const char *p;
|
2009-06-24 20:42:28 +08:00
|
|
|
char buf[256];
|
2009-05-08 18:34:18 +08:00
|
|
|
int is_udp;
|
2009-06-24 20:42:28 +08:00
|
|
|
char *end;
|
2009-05-26 19:03:27 +08:00
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
p = redir_str;
|
2009-06-24 20:42:32 +08:00
|
|
|
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2009-04-22 03:56:44 +08:00
|
|
|
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
|
2008-11-01 03:10:00 +08:00
|
|
|
is_udp = 0;
|
|
|
|
} else if (!strcmp(buf, "udp")) {
|
|
|
|
is_udp = 1;
|
|
|
|
} else {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
if (!legacy_format) {
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
host_port = strtol(buf, &end, 0);
|
|
|
|
if (*end != '\0' || host_port < 1 || host_port > 65535) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-05-08 18:34:18 +08:00
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
guest_port = strtol(p, &end, 0);
|
|
|
|
if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
|
2009-04-22 03:56:44 +08:00
|
|
|
goto fail_syntax;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
|
|
|
|
guest_port) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("could not set up host forwarding rule '%s'\n",
|
|
|
|
redir_str);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-04-22 03:56:44 +08:00
|
|
|
|
|
|
|
fail_syntax:
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid host forwarding rule '%s'\n", redir_str);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:15 +08:00
|
|
|
void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict)
|
2009-05-08 18:34:18 +08:00
|
|
|
{
|
2009-06-24 20:42:32 +08:00
|
|
|
const char *redir_str;
|
|
|
|
SlirpState *s;
|
2009-08-29 02:27:15 +08:00
|
|
|
const char *arg1 = qdict_get_str(qdict, "arg1");
|
|
|
|
const char *arg2 = qdict_get_try_str(qdict, "arg2");
|
|
|
|
const char *arg3 = qdict_get_try_str(qdict, "arg3");
|
2009-06-24 20:42:32 +08:00
|
|
|
|
|
|
|
if (arg2) {
|
|
|
|
s = slirp_lookup(mon, arg1, arg2);
|
|
|
|
redir_str = arg3;
|
|
|
|
} else {
|
|
|
|
s = slirp_lookup(mon, NULL, NULL);
|
|
|
|
redir_str = arg1;
|
|
|
|
}
|
|
|
|
if (s) {
|
2009-10-06 19:16:58 +08:00
|
|
|
slirp_hostfwd(s, redir_str, 0);
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
|
|
|
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
int net_slirp_redir(const char *redir_str)
|
2009-06-24 20:42:28 +08:00
|
|
|
{
|
|
|
|
struct slirp_config_str *config;
|
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
2009-06-24 20:42:28 +08:00
|
|
|
config = qemu_malloc(sizeof(*config));
|
|
|
|
pstrcpy(config->str, sizeof(config->str), redir_str);
|
2009-06-24 20:42:28 +08:00
|
|
|
config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
|
2009-06-24 20:42:28 +08:00
|
|
|
config->next = slirp_configs;
|
|
|
|
slirp_configs = config;
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifndef _WIN32
|
|
|
|
|
|
|
|
/* automatic user mode samba server configuration */
|
2009-06-24 20:42:31 +08:00
|
|
|
static void slirp_smb_cleanup(SlirpState *s)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
char cmd[128];
|
2009-06-24 20:42:31 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
if (s->smb_dir[0] != '\0') {
|
|
|
|
snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
|
|
|
|
system(cmd);
|
|
|
|
s->smb_dir[0] = '\0';
|
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_smb(SlirpState* s, const char *exported_dir,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
struct in_addr vserver_addr)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
static int instance;
|
|
|
|
char smb_conf[128];
|
|
|
|
char smb_cmdline[128];
|
2008-11-01 03:10:00 +08:00
|
|
|
FILE *f;
|
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
|
|
|
|
(long)getpid(), instance++);
|
|
|
|
if (mkdir(s->smb_dir, 0700) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("could not create samba server dir '%s'\n", s->smb_dir);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
|
2008-11-01 03:10:00 +08:00
|
|
|
|
|
|
|
f = fopen(smb_conf, "w");
|
|
|
|
if (!f) {
|
2009-06-24 20:42:31 +08:00
|
|
|
slirp_smb_cleanup(s);
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("could not create samba server configuration file '%s'\n",
|
|
|
|
smb_conf);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
fprintf(f,
|
|
|
|
"[global]\n"
|
|
|
|
"private dir=%s\n"
|
|
|
|
"smb ports=0\n"
|
|
|
|
"socket address=127.0.0.1\n"
|
|
|
|
"pid directory=%s\n"
|
|
|
|
"lock directory=%s\n"
|
|
|
|
"log file=%s/log.smbd\n"
|
|
|
|
"smb passwd file=%s/smbpasswd\n"
|
|
|
|
"security = share\n"
|
|
|
|
"[qemu]\n"
|
|
|
|
"path=%s\n"
|
|
|
|
"read only=no\n"
|
|
|
|
"guest ok=yes\n",
|
2009-06-24 20:42:31 +08:00
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
2008-11-01 03:10:00 +08:00
|
|
|
exported_dir
|
|
|
|
);
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
|
|
|
|
SMBD_COMMAND, smb_conf);
|
|
|
|
|
2009-07-22 23:03:52 +08:00
|
|
|
if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) {
|
2009-06-24 20:42:31 +08:00
|
|
|
slirp_smb_cleanup(s);
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("conflicting/invalid smbserver address\n");
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
/* automatic user mode samba server configuration (legacy interface) */
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
int net_slirp_smb(const char *exported_dir)
|
2009-05-08 18:34:18 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr vserver_addr = { .s_addr = 0 };
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
if (legacy_smb_export) {
|
2009-05-08 18:34:18 +08:00
|
|
|
fprintf(stderr, "-smb given twice\n");
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
2009-06-24 20:42:28 +08:00
|
|
|
legacy_smb_export = exported_dir;
|
2009-09-12 15:36:22 +08:00
|
|
|
if (!QTAILQ_EMPTY(&slirp_stacks)) {
|
2009-10-06 19:16:58 +08:00
|
|
|
return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
vserver_addr);
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-05-08 18:34:18 +08:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#endif /* !defined(_WIN32) */
|
2009-05-08 18:34:18 +08:00
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
struct GuestFwd {
|
2009-02-16 23:34:18 +08:00
|
|
|
CharDriverState *hd;
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr server;
|
2009-02-16 23:34:18 +08:00
|
|
|
int port;
|
2009-06-24 20:42:31 +08:00
|
|
|
Slirp *slirp;
|
2009-03-07 23:32:56 +08:00
|
|
|
};
|
2009-02-16 23:34:18 +08:00
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
static int guestfwd_can_read(void *opaque)
|
2009-02-16 23:34:18 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct GuestFwd *fwd = opaque;
|
2009-06-24 20:42:31 +08:00
|
|
|
return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
|
2009-02-16 23:34:18 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
|
2009-02-16 23:34:18 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct GuestFwd *fwd = opaque;
|
2009-06-24 20:42:31 +08:00
|
|
|
slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
|
2009-02-16 23:34:18 +08:00
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str,
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
int legacy_format)
|
2009-06-24 20:42:28 +08:00
|
|
|
{
|
2009-06-24 20:42:28 +08:00
|
|
|
struct in_addr server = { .s_addr = 0 };
|
|
|
|
struct GuestFwd *fwd;
|
|
|
|
const char *p;
|
|
|
|
char buf[128];
|
|
|
|
char *end;
|
2009-06-24 20:42:28 +08:00
|
|
|
int port;
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
p = config_str;
|
|
|
|
if (legacy_format) {
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (strcmp(buf, "tcp") && buf[0] != '\0') {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &server)) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port = strtol(buf, &end, 10);
|
|
|
|
if (*end != '\0' || port < 1 || port > 65535) {
|
|
|
|
goto fail_syntax;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
fwd = qemu_malloc(sizeof(struct GuestFwd));
|
|
|
|
snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port);
|
|
|
|
fwd->hd = qemu_chr_open(buf, p, NULL);
|
|
|
|
if (!fwd->hd) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("could not open guest forwarding device '%s'\n", buf);
|
2009-06-24 20:42:28 +08:00
|
|
|
qemu_free(fwd);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
|
|
|
|
2009-07-22 23:03:52 +08:00
|
|
|
if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("conflicting/invalid host:port in guest forwarding "
|
|
|
|
"rule '%s'\n", config_str);
|
2009-06-24 20:42:28 +08:00
|
|
|
qemu_free(fwd);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
2009-07-22 23:03:52 +08:00
|
|
|
fwd->server = server;
|
|
|
|
fwd->port = port;
|
|
|
|
fwd->slirp = s->slirp;
|
|
|
|
|
2009-06-24 20:42:28 +08:00
|
|
|
qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
|
|
|
|
NULL, fwd);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-06-24 20:42:28 +08:00
|
|
|
|
|
|
|
fail_syntax:
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid guest forwarding rule '%s'\n", config_str);
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-06-24 20:42:28 +08:00
|
|
|
}
|
|
|
|
|
2009-06-24 20:42:29 +08:00
|
|
|
void do_info_usernet(Monitor *mon)
|
|
|
|
{
|
2009-06-24 20:42:31 +08:00
|
|
|
SlirpState *s;
|
2009-06-24 20:42:31 +08:00
|
|
|
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_FOREACH(s, &slirp_stacks, entry) {
|
2009-06-24 20:42:31 +08:00
|
|
|
monitor_printf(mon, "VLAN %d (%s):\n", s->vc->vlan->id, s->vc->name);
|
|
|
|
slirp_connection_info(s->slirp, mon);
|
2009-06-24 20:42:31 +08:00
|
|
|
}
|
2009-06-24 20:42:29 +08:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#endif /* CONFIG_SLIRP */
|
|
|
|
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
|
|
|
|
typedef struct TAPState {
|
|
|
|
VLANClientState *vc;
|
|
|
|
int fd;
|
|
|
|
char down_script[1024];
|
2009-01-14 03:15:55 +08:00
|
|
|
char down_script_arg[128];
|
2009-05-18 19:05:44 +08:00
|
|
|
uint8_t buf[4096];
|
2009-06-19 01:21:31 +08:00
|
|
|
unsigned int read_poll : 1;
|
2009-06-19 01:21:32 +08:00
|
|
|
unsigned int write_poll : 1;
|
2008-11-01 03:10:00 +08:00
|
|
|
} TAPState;
|
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
static int launch_script(const char *setup_script, const char *ifname, int fd);
|
|
|
|
|
2009-06-19 01:21:31 +08:00
|
|
|
static int tap_can_send(void *opaque);
|
|
|
|
static void tap_send(void *opaque);
|
2009-06-19 01:21:32 +08:00
|
|
|
static void tap_writable(void *opaque);
|
2009-06-19 01:21:31 +08:00
|
|
|
|
|
|
|
static void tap_update_fd_handler(TAPState *s)
|
|
|
|
{
|
|
|
|
qemu_set_fd_handler2(s->fd,
|
|
|
|
s->read_poll ? tap_can_send : NULL,
|
|
|
|
s->read_poll ? tap_send : NULL,
|
2009-06-19 01:21:32 +08:00
|
|
|
s->write_poll ? tap_writable : NULL,
|
2009-06-19 01:21:31 +08:00
|
|
|
s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tap_read_poll(TAPState *s, int enable)
|
|
|
|
{
|
|
|
|
s->read_poll = !!enable;
|
|
|
|
tap_update_fd_handler(s);
|
|
|
|
}
|
|
|
|
|
2009-06-19 01:21:32 +08:00
|
|
|
static void tap_write_poll(TAPState *s, int enable)
|
|
|
|
{
|
|
|
|
s->write_poll = !!enable;
|
|
|
|
tap_update_fd_handler(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void tap_writable(void *opaque)
|
|
|
|
{
|
|
|
|
TAPState *s = opaque;
|
|
|
|
|
|
|
|
tap_write_poll(s, 0);
|
|
|
|
|
|
|
|
qemu_flush_queued_packets(s->vc);
|
|
|
|
}
|
|
|
|
|
2009-05-18 20:33:03 +08:00
|
|
|
static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov,
|
2008-12-18 03:17:17 +08:00
|
|
|
int iovcnt)
|
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
TAPState *s = vc->opaque;
|
2008-12-18 03:17:17 +08:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
do {
|
|
|
|
len = writev(s->fd, iov, iovcnt);
|
2009-06-19 01:21:32 +08:00
|
|
|
} while (len == -1 && errno == EINTR);
|
|
|
|
|
|
|
|
if (len == -1 && errno == EAGAIN) {
|
|
|
|
tap_write_poll(s, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
2008-12-18 03:17:17 +08:00
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
TAPState *s = vc->opaque;
|
2009-05-18 20:40:55 +08:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
do {
|
|
|
|
len = write(s->fd, buf, size);
|
|
|
|
} while (len == -1 && (errno == EINTR || errno == EAGAIN));
|
|
|
|
|
|
|
|
return len;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-04-29 16:50:32 +08:00
|
|
|
static int tap_can_send(void *opaque)
|
|
|
|
{
|
|
|
|
TAPState *s = opaque;
|
|
|
|
|
|
|
|
return qemu_can_send_packet(s->vc);
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifdef __sun__
|
2009-04-29 16:43:37 +08:00
|
|
|
static ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
|
|
|
|
{
|
2008-11-01 03:10:00 +08:00
|
|
|
struct strbuf sbuf;
|
|
|
|
int f = 0;
|
2009-04-29 16:43:37 +08:00
|
|
|
|
|
|
|
sbuf.maxlen = maxlen;
|
2009-04-14 00:31:01 +08:00
|
|
|
sbuf.buf = (char *)buf;
|
2009-04-29 16:43:37 +08:00
|
|
|
|
|
|
|
return getmsg(tapfd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
|
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
#else
|
2009-04-29 16:43:37 +08:00
|
|
|
static ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
|
|
|
|
{
|
|
|
|
return read(tapfd, buf, maxlen);
|
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
#endif
|
2009-04-29 16:43:37 +08:00
|
|
|
|
2009-06-19 01:21:35 +08:00
|
|
|
static void tap_send_completed(VLANClientState *vc, ssize_t len)
|
2009-04-29 20:30:24 +08:00
|
|
|
{
|
|
|
|
TAPState *s = vc->opaque;
|
2009-06-19 01:21:31 +08:00
|
|
|
tap_read_poll(s, 1);
|
2009-04-29 20:30:24 +08:00
|
|
|
}
|
|
|
|
|
2009-04-29 16:43:37 +08:00
|
|
|
static void tap_send(void *opaque)
|
|
|
|
{
|
|
|
|
TAPState *s = opaque;
|
|
|
|
int size;
|
|
|
|
|
2009-04-29 20:30:24 +08:00
|
|
|
do {
|
|
|
|
size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
|
|
|
|
if (size <= 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = qemu_send_packet_async(s->vc, s->buf, size, tap_send_completed);
|
|
|
|
if (size == 0) {
|
2009-06-19 01:21:31 +08:00
|
|
|
tap_read_poll(s, 0);
|
2009-04-29 20:30:24 +08:00
|
|
|
}
|
|
|
|
} while (size > 0);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-19 01:21:34 +08:00
|
|
|
#ifdef TUNSETSNDBUF
|
2009-06-30 17:02:57 +08:00
|
|
|
/* sndbuf should be set to a value lower than the tx queue
|
|
|
|
* capacity of any destination network interface.
|
|
|
|
* Ethernet NICs generally have txqueuelen=1000, so 1Mb is
|
|
|
|
* a good default, given a 1500 byte MTU.
|
|
|
|
*/
|
|
|
|
#define TAP_DEFAULT_SNDBUF 1024*1024
|
|
|
|
|
2009-10-06 19:17:08 +08:00
|
|
|
static int tap_set_sndbuf(TAPState *s, QemuOpts *opts)
|
2009-06-30 17:02:57 +08:00
|
|
|
{
|
2009-10-06 19:17:08 +08:00
|
|
|
int sndbuf;
|
2009-06-30 17:02:57 +08:00
|
|
|
|
2009-10-06 19:17:08 +08:00
|
|
|
sndbuf = qemu_opt_get_size(opts, "sndbuf", TAP_DEFAULT_SNDBUF);
|
2009-06-30 17:02:57 +08:00
|
|
|
if (!sndbuf) {
|
|
|
|
sndbuf = INT_MAX;
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:17:08 +08:00
|
|
|
if (ioctl(s->fd, TUNSETSNDBUF, &sndbuf) == -1 && qemu_opt_get(opts, "sndbuf")) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("TUNSETSNDBUF ioctl failed: %s\n", strerror(errno));
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return -1;
|
2009-06-19 01:21:34 +08:00
|
|
|
}
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-06-30 17:02:57 +08:00
|
|
|
}
|
2009-06-19 01:21:34 +08:00
|
|
|
#else
|
2009-10-06 19:17:08 +08:00
|
|
|
static int tap_set_sndbuf(TAPState *s, QemuOpts *opts)
|
2009-06-30 17:02:57 +08:00
|
|
|
{
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
return 0;
|
2009-06-19 01:21:34 +08:00
|
|
|
}
|
2009-06-30 17:02:57 +08:00
|
|
|
#endif /* TUNSETSNDBUF */
|
2009-06-19 01:21:34 +08:00
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
static void tap_cleanup(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
TAPState *s = vc->opaque;
|
|
|
|
|
2009-06-19 01:21:30 +08:00
|
|
|
qemu_purge_queued_packets(vc);
|
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
if (s->down_script[0])
|
|
|
|
launch_script(s->down_script, s->down_script_arg, s->fd);
|
|
|
|
|
2009-06-19 01:21:31 +08:00
|
|
|
tap_read_poll(s, 0);
|
2009-06-19 01:21:32 +08:00
|
|
|
tap_write_poll(s, 0);
|
2009-04-18 01:11:08 +08:00
|
|
|
close(s->fd);
|
|
|
|
qemu_free(s);
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
/* fd support */
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static TAPState *net_tap_fd_init(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
|
|
|
int fd)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
TAPState *s;
|
|
|
|
|
|
|
|
s = qemu_mallocz(sizeof(TAPState));
|
|
|
|
s->fd = fd;
|
2009-05-18 19:55:27 +08:00
|
|
|
s->vc = qemu_new_vlan_client(vlan, model, name, NULL, tap_receive,
|
|
|
|
tap_receive_iov, tap_cleanup, s);
|
2009-06-19 01:21:31 +08:00
|
|
|
tap_read_poll(s, 1);
|
2009-01-08 01:46:21 +08:00
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd);
|
2008-11-01 03:10:00 +08:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2009-07-27 22:12:56 +08:00
|
|
|
#if defined (CONFIG_BSD) || defined (__FreeBSD_kernel__)
|
2008-11-01 03:10:00 +08:00
|
|
|
static int tap_open(char *ifname, int ifname_size)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
char *dev;
|
|
|
|
struct stat s;
|
|
|
|
|
|
|
|
TFR(fd = open("/dev/tap", O_RDWR));
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fstat(fd, &s);
|
|
|
|
dev = devname(s.st_rdev, S_IFCHR);
|
|
|
|
pstrcpy(ifname, ifname_size, dev);
|
|
|
|
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
#elif defined(__sun__)
|
|
|
|
#define TUNNEWPPA (('T'<<16) | 0x0001)
|
|
|
|
/*
|
|
|
|
* Allocate TAP device, returns opened fd.
|
|
|
|
* Stores dev name in the first arg(must be large enough).
|
|
|
|
*/
|
2009-04-14 00:31:01 +08:00
|
|
|
static int tap_alloc(char *dev, size_t dev_size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
int tap_fd, if_fd, ppa = -1;
|
|
|
|
static int ip_fd = 0;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
static int arp_fd = 0;
|
|
|
|
int ip_muxid, arp_muxid;
|
|
|
|
struct strioctl strioc_if, strioc_ppa;
|
|
|
|
int link_type = I_PLINK;;
|
|
|
|
struct lifreq ifr;
|
|
|
|
char actual_name[32] = "";
|
|
|
|
|
|
|
|
memset(&ifr, 0x0, sizeof(ifr));
|
|
|
|
|
|
|
|
if( *dev ){
|
|
|
|
ptr = dev;
|
2008-11-23 04:04:24 +08:00
|
|
|
while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++;
|
2008-11-01 03:10:00 +08:00
|
|
|
ppa = atoi(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if IP device was opened */
|
|
|
|
if( ip_fd )
|
|
|
|
close(ip_fd);
|
|
|
|
|
|
|
|
TFR(ip_fd = open("/dev/udp", O_RDWR, 0));
|
|
|
|
if (ip_fd < 0) {
|
|
|
|
syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TFR(tap_fd = open("/dev/tap", O_RDWR, 0));
|
|
|
|
if (tap_fd < 0) {
|
|
|
|
syslog(LOG_ERR, "Can't open /dev/tap");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Assign a new PPA and get its unit number. */
|
|
|
|
strioc_ppa.ic_cmd = TUNNEWPPA;
|
|
|
|
strioc_ppa.ic_timout = 0;
|
|
|
|
strioc_ppa.ic_len = sizeof(ppa);
|
|
|
|
strioc_ppa.ic_dp = (char *)&ppa;
|
|
|
|
if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0)
|
|
|
|
syslog (LOG_ERR, "Can't assign new interface");
|
|
|
|
|
|
|
|
TFR(if_fd = open("/dev/tap", O_RDWR, 0));
|
|
|
|
if (if_fd < 0) {
|
|
|
|
syslog(LOG_ERR, "Can't open /dev/tap (2)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if(ioctl(if_fd, I_PUSH, "ip") < 0){
|
|
|
|
syslog(LOG_ERR, "Can't push IP module");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
|
|
|
|
syslog(LOG_ERR, "Can't get flags\n");
|
|
|
|
|
|
|
|
snprintf (actual_name, 32, "tap%d", ppa);
|
|
|
|
pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
|
|
|
|
|
|
|
|
ifr.lifr_ppa = ppa;
|
|
|
|
/* Assign ppa according to the unit number returned by tun device */
|
|
|
|
|
|
|
|
if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
|
|
|
|
syslog (LOG_ERR, "Can't set PPA %d", ppa);
|
|
|
|
if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
|
|
|
|
syslog (LOG_ERR, "Can't get flags\n");
|
|
|
|
/* Push arp module to if_fd */
|
|
|
|
if (ioctl (if_fd, I_PUSH, "arp") < 0)
|
|
|
|
syslog (LOG_ERR, "Can't push ARP module (2)");
|
|
|
|
|
|
|
|
/* Push arp module to ip_fd */
|
|
|
|
if (ioctl (ip_fd, I_POP, NULL) < 0)
|
|
|
|
syslog (LOG_ERR, "I_POP failed\n");
|
|
|
|
if (ioctl (ip_fd, I_PUSH, "arp") < 0)
|
|
|
|
syslog (LOG_ERR, "Can't push ARP module (3)\n");
|
|
|
|
/* Open arp_fd */
|
|
|
|
TFR(arp_fd = open ("/dev/tap", O_RDWR, 0));
|
|
|
|
if (arp_fd < 0)
|
|
|
|
syslog (LOG_ERR, "Can't open %s\n", "/dev/tap");
|
|
|
|
|
|
|
|
/* Set ifname to arp */
|
|
|
|
strioc_if.ic_cmd = SIOCSLIFNAME;
|
|
|
|
strioc_if.ic_timout = 0;
|
|
|
|
strioc_if.ic_len = sizeof(ifr);
|
|
|
|
strioc_if.ic_dp = (char *)𝔦
|
|
|
|
if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
|
|
|
|
syslog (LOG_ERR, "Can't set ifname to arp\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){
|
|
|
|
syslog(LOG_ERR, "Can't link TAP device to IP");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0)
|
|
|
|
syslog (LOG_ERR, "Can't link TAP device to ARP");
|
|
|
|
|
|
|
|
close (if_fd);
|
|
|
|
|
|
|
|
memset(&ifr, 0x0, sizeof(ifr));
|
|
|
|
pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
|
|
|
|
ifr.lifr_ip_muxid = ip_muxid;
|
|
|
|
ifr.lifr_arp_muxid = arp_muxid;
|
|
|
|
|
|
|
|
if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0)
|
|
|
|
{
|
|
|
|
ioctl (ip_fd, I_PUNLINK , arp_muxid);
|
|
|
|
ioctl (ip_fd, I_PUNLINK, ip_muxid);
|
|
|
|
syslog (LOG_ERR, "Can't set multiplexor id");
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(dev, dev_size, "tap%d", ppa);
|
|
|
|
return tap_fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tap_open(char *ifname, int ifname_size)
|
|
|
|
{
|
|
|
|
char dev[10]="";
|
|
|
|
int fd;
|
|
|
|
if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){
|
|
|
|
fprintf(stderr, "Cannot allocate TAP device\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pstrcpy(ifname, ifname_size, dev);
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
return fd;
|
|
|
|
}
|
2008-11-18 09:42:22 +08:00
|
|
|
#elif defined (_AIX)
|
|
|
|
static int tap_open(char *ifname, int ifname_size)
|
|
|
|
{
|
|
|
|
fprintf (stderr, "no tap on AIX\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
#else
|
|
|
|
static int tap_open(char *ifname, int ifname_size)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
|
|
int fd, ret;
|
|
|
|
|
|
|
|
TFR(fd = open("/dev/net/tun", O_RDWR));
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
|
|
|
if (ifname[0] != '\0')
|
|
|
|
pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
|
|
|
|
else
|
|
|
|
pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
|
|
|
|
ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
|
|
|
|
if (ret != 0) {
|
|
|
|
fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n");
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pstrcpy(ifname, ifname_size, ifr.ifr_name);
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int launch_script(const char *setup_script, const char *ifname, int fd)
|
|
|
|
{
|
2009-05-08 18:34:17 +08:00
|
|
|
sigset_t oldmask, mask;
|
2008-11-01 03:10:00 +08:00
|
|
|
int pid, status;
|
|
|
|
char *args[3];
|
|
|
|
char **parg;
|
|
|
|
|
2009-05-08 18:34:17 +08:00
|
|
|
sigemptyset(&mask);
|
|
|
|
sigaddset(&mask, SIGCHLD);
|
|
|
|
sigprocmask(SIG_BLOCK, &mask, &oldmask);
|
|
|
|
|
|
|
|
/* try to launch network script */
|
|
|
|
pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
int open_max = sysconf(_SC_OPEN_MAX), i;
|
|
|
|
|
|
|
|
for (i = 0; i < open_max; i++) {
|
|
|
|
if (i != STDIN_FILENO &&
|
|
|
|
i != STDOUT_FILENO &&
|
|
|
|
i != STDERR_FILENO &&
|
|
|
|
i != fd) {
|
|
|
|
close(i);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
}
|
2009-05-08 18:34:17 +08:00
|
|
|
parg = args;
|
|
|
|
*parg++ = (char *)setup_script;
|
|
|
|
*parg++ = (char *)ifname;
|
|
|
|
*parg++ = NULL;
|
|
|
|
execv(setup_script, args);
|
|
|
|
_exit(1);
|
|
|
|
} else if (pid > 0) {
|
|
|
|
while (waitpid(pid, &status, 0) != pid) {
|
|
|
|
/* loop */
|
|
|
|
}
|
|
|
|
sigprocmask(SIG_SETMASK, &oldmask, NULL);
|
|
|
|
|
|
|
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stderr, "%s: could not launch network script\n", setup_script);
|
|
|
|
return -1;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-19 01:21:33 +08:00
|
|
|
static TAPState *net_tap_init(VLANState *vlan, const char *model,
|
|
|
|
const char *name, const char *ifname1,
|
|
|
|
const char *setup_script, const char *down_script)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
TAPState *s;
|
|
|
|
int fd;
|
|
|
|
char ifname[128];
|
|
|
|
|
|
|
|
if (ifname1 != NULL)
|
|
|
|
pstrcpy(ifname, sizeof(ifname), ifname1);
|
|
|
|
else
|
|
|
|
ifname[0] = '\0';
|
|
|
|
TFR(fd = tap_open(ifname, sizeof(ifname)));
|
|
|
|
if (fd < 0)
|
2009-06-19 01:21:33 +08:00
|
|
|
return NULL;
|
2008-11-01 03:10:00 +08:00
|
|
|
|
|
|
|
if (!setup_script || !strcmp(setup_script, "no"))
|
|
|
|
setup_script = "";
|
2009-06-19 01:21:33 +08:00
|
|
|
if (setup_script[0] != '\0' &&
|
|
|
|
launch_script(setup_script, ifname, fd)) {
|
|
|
|
return NULL;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
s = net_tap_fd_init(vlan, model, name, fd);
|
2008-11-01 03:10:00 +08:00
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
2009-01-08 01:46:21 +08:00
|
|
|
"ifname=%s,script=%s,downscript=%s",
|
|
|
|
ifname, setup_script, down_script);
|
2009-01-14 03:15:55 +08:00
|
|
|
if (down_script && strcmp(down_script, "no")) {
|
2008-11-01 03:10:00 +08:00
|
|
|
snprintf(s->down_script, sizeof(s->down_script), "%s", down_script);
|
2009-01-14 03:15:55 +08:00
|
|
|
snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
|
|
|
|
}
|
2009-06-19 01:21:33 +08:00
|
|
|
return s;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !_WIN32 */
|
|
|
|
|
|
|
|
#if defined(CONFIG_VDE)
|
|
|
|
typedef struct VDEState {
|
|
|
|
VLANClientState *vc;
|
|
|
|
VDECONN *vde;
|
|
|
|
} VDEState;
|
|
|
|
|
|
|
|
static void vde_to_qemu(void *opaque)
|
|
|
|
{
|
|
|
|
VDEState *s = opaque;
|
|
|
|
uint8_t buf[4096];
|
|
|
|
int size;
|
|
|
|
|
2009-05-04 05:40:54 +08:00
|
|
|
size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (size > 0) {
|
|
|
|
qemu_send_packet(s->vc, buf, size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t vde_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
VDEState *s = vc->opaque;
|
2009-06-11 09:24:44 +08:00
|
|
|
ssize_t ret;
|
2009-05-18 20:40:55 +08:00
|
|
|
|
|
|
|
do {
|
|
|
|
ret = vde_send(s->vde, (const char *)buf, size, 0);
|
|
|
|
} while (ret < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
return ret;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
static void vde_cleanup(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
VDEState *s = vc->opaque;
|
|
|
|
qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL);
|
|
|
|
vde_close(s->vde);
|
|
|
|
qemu_free(s);
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static int net_vde_init(VLANState *vlan, const char *model,
|
|
|
|
const char *name, const char *sock,
|
2009-01-08 01:42:25 +08:00
|
|
|
int port, const char *group, int mode)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
VDEState *s;
|
|
|
|
char *init_group = strlen(group) ? (char *)group : NULL;
|
|
|
|
char *init_sock = strlen(sock) ? (char *)sock : NULL;
|
|
|
|
|
|
|
|
struct vde_open_args args = {
|
|
|
|
.port = port,
|
|
|
|
.group = init_group,
|
|
|
|
.mode = mode,
|
|
|
|
};
|
|
|
|
|
|
|
|
s = qemu_mallocz(sizeof(VDEState));
|
2009-05-04 05:40:54 +08:00
|
|
|
s->vde = vde_open(init_sock, (char *)"QEMU", &args);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (!s->vde){
|
|
|
|
free(s);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-05-18 20:33:03 +08:00
|
|
|
s->vc = qemu_new_vlan_client(vlan, model, name, NULL, vde_receive,
|
2009-04-18 01:11:08 +08:00
|
|
|
NULL, vde_cleanup, s);
|
2008-11-01 03:10:00 +08:00
|
|
|
qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
|
2009-01-08 01:46:21 +08:00
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str), "sock=%s,fd=%d",
|
2008-11-01 03:10:00 +08:00
|
|
|
sock, vde_datafd(s->vde));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* network connection */
|
|
|
|
typedef struct NetSocketState {
|
|
|
|
VLANClientState *vc;
|
|
|
|
int fd;
|
|
|
|
int state; /* 0 = getting length, 1 = getting data */
|
2009-02-28 03:54:01 +08:00
|
|
|
unsigned int index;
|
|
|
|
unsigned int packet_len;
|
2008-11-01 03:10:00 +08:00
|
|
|
uint8_t buf[4096];
|
|
|
|
struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
|
|
|
|
} NetSocketState;
|
|
|
|
|
|
|
|
typedef struct NetSocketListenState {
|
|
|
|
VLANState *vlan;
|
2009-01-08 01:42:25 +08:00
|
|
|
char *model;
|
2009-01-08 01:48:51 +08:00
|
|
|
char *name;
|
2008-11-01 03:10:00 +08:00
|
|
|
int fd;
|
|
|
|
} NetSocketListenState;
|
|
|
|
|
|
|
|
/* XXX: we consider we can send the whole packet without blocking */
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t net_socket_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
NetSocketState *s = vc->opaque;
|
2008-11-01 03:10:00 +08:00
|
|
|
uint32_t len;
|
|
|
|
len = htonl(size);
|
|
|
|
|
|
|
|
send_all(s->fd, (const uint8_t *)&len, sizeof(len));
|
2009-05-18 20:40:55 +08:00
|
|
|
return send_all(s->fd, buf, size);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t net_socket_receive_dgram(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
NetSocketState *s = vc->opaque;
|
2009-05-18 20:40:55 +08:00
|
|
|
|
2009-06-13 19:05:27 +08:00
|
|
|
return sendto(s->fd, (const void *)buf, size, 0,
|
2009-05-18 20:40:55 +08:00
|
|
|
(struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void net_socket_send(void *opaque)
|
|
|
|
{
|
|
|
|
NetSocketState *s = opaque;
|
2009-02-28 03:54:01 +08:00
|
|
|
int size, err;
|
|
|
|
unsigned l;
|
2008-11-01 03:10:00 +08:00
|
|
|
uint8_t buf1[4096];
|
|
|
|
const uint8_t *buf;
|
|
|
|
|
2009-06-13 16:44:31 +08:00
|
|
|
size = recv(s->fd, (void *)buf1, sizeof(buf1), 0);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (size < 0) {
|
|
|
|
err = socket_error();
|
|
|
|
if (err != EWOULDBLOCK)
|
|
|
|
goto eoc;
|
|
|
|
} else if (size == 0) {
|
|
|
|
/* end of connection */
|
|
|
|
eoc:
|
|
|
|
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
|
|
|
closesocket(s->fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
buf = buf1;
|
|
|
|
while (size > 0) {
|
|
|
|
/* reassemble a packet from the network */
|
|
|
|
switch(s->state) {
|
|
|
|
case 0:
|
|
|
|
l = 4 - s->index;
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
|
|
|
memcpy(s->buf + s->index, buf, l);
|
|
|
|
buf += l;
|
|
|
|
size -= l;
|
|
|
|
s->index += l;
|
|
|
|
if (s->index == 4) {
|
|
|
|
/* got length */
|
|
|
|
s->packet_len = ntohl(*(uint32_t *)s->buf);
|
|
|
|
s->index = 0;
|
|
|
|
s->state = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
l = s->packet_len - s->index;
|
|
|
|
if (l > size)
|
|
|
|
l = size;
|
2009-02-28 03:54:01 +08:00
|
|
|
if (s->index + l <= sizeof(s->buf)) {
|
|
|
|
memcpy(s->buf + s->index, buf, l);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "serious error: oversized packet received,"
|
|
|
|
"connection terminated.\n");
|
|
|
|
s->state = 0;
|
|
|
|
goto eoc;
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
s->index += l;
|
|
|
|
buf += l;
|
|
|
|
size -= l;
|
|
|
|
if (s->index >= s->packet_len) {
|
|
|
|
qemu_send_packet(s->vc, s->buf, s->packet_len);
|
|
|
|
s->index = 0;
|
|
|
|
s->state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_socket_send_dgram(void *opaque)
|
|
|
|
{
|
|
|
|
NetSocketState *s = opaque;
|
|
|
|
int size;
|
|
|
|
|
2009-06-13 16:44:31 +08:00
|
|
|
size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (size < 0)
|
|
|
|
return;
|
|
|
|
if (size == 0) {
|
|
|
|
/* end of connection */
|
|
|
|
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
qemu_send_packet(s->vc, s->buf, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
|
|
|
|
{
|
|
|
|
struct ip_mreq imr;
|
|
|
|
int fd;
|
|
|
|
int val, ret;
|
|
|
|
if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
|
|
|
|
fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
|
|
|
|
inet_ntoa(mcastaddr->sin_addr),
|
|
|
|
(int)ntohl(mcastaddr->sin_addr.s_addr));
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
}
|
|
|
|
fd = socket(PF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
perror("socket(PF_INET, SOCK_DGRAM)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = 1;
|
|
|
|
ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
|
|
|
|
(const char *)&val, sizeof(val));
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("bind");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add host to multicast group */
|
|
|
|
imr.imr_multiaddr = mcastaddr->sin_addr;
|
|
|
|
imr.imr_interface.s_addr = htonl(INADDR_ANY);
|
|
|
|
|
|
|
|
ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
|
|
|
(const char *)&imr, sizeof(struct ip_mreq));
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("setsockopt(IP_ADD_MEMBERSHIP)");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Force mcast msgs to loopback (eg. several QEMUs in same host */
|
|
|
|
val = 1;
|
|
|
|
ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
|
|
|
|
(const char *)&val, sizeof(val));
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
socket_set_nonblock(fd);
|
|
|
|
return fd;
|
|
|
|
fail:
|
|
|
|
if (fd >= 0)
|
|
|
|
closesocket(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
static void net_socket_cleanup(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
NetSocketState *s = vc->opaque;
|
|
|
|
qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
|
|
|
|
close(s->fd);
|
|
|
|
qemu_free(s);
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
int fd, int is_connected)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
struct sockaddr_in saddr;
|
|
|
|
int newfd;
|
|
|
|
socklen_t saddr_len;
|
|
|
|
NetSocketState *s;
|
|
|
|
|
|
|
|
/* fd passed: multicast: "learn" dgram_dst address from bound address and save it
|
|
|
|
* Because this may be "shared" socket from a "master" process, datagrams would be recv()
|
|
|
|
* by ONLY ONE process: we must "clone" this dgram socket --jjo
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (is_connected) {
|
|
|
|
if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
|
|
|
|
/* must be bound */
|
|
|
|
if (saddr.sin_addr.s_addr==0) {
|
|
|
|
fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
|
|
|
|
fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* clone dgram socket */
|
|
|
|
newfd = net_socket_mcast_create(&saddr);
|
|
|
|
if (newfd < 0) {
|
|
|
|
/* error already reported by net_socket_mcast_create() */
|
|
|
|
close(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* clone newfd to fd, close newfd */
|
|
|
|
dup2(newfd, fd);
|
|
|
|
close(newfd);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
|
|
|
|
fd, strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s = qemu_mallocz(sizeof(NetSocketState));
|
|
|
|
s->fd = fd;
|
|
|
|
|
2009-05-18 19:55:27 +08:00
|
|
|
s->vc = qemu_new_vlan_client(vlan, model, name, NULL, net_socket_receive_dgram,
|
2009-04-18 01:11:08 +08:00
|
|
|
NULL, net_socket_cleanup, s);
|
2008-11-01 03:10:00 +08:00
|
|
|
qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
|
|
|
|
|
|
|
|
/* mcast: save bound address as dst */
|
|
|
|
if (is_connected) s->dgram_dst=saddr;
|
|
|
|
|
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
|
|
|
"socket: fd=%d (%s mcast=%s:%d)",
|
|
|
|
fd, is_connected? "cloned" : "",
|
|
|
|
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_socket_connect(void *opaque)
|
|
|
|
{
|
|
|
|
NetSocketState *s = opaque;
|
|
|
|
qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
int fd, int is_connected)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
NetSocketState *s;
|
|
|
|
s = qemu_mallocz(sizeof(NetSocketState));
|
|
|
|
s->fd = fd;
|
2009-05-18 19:55:27 +08:00
|
|
|
s->vc = qemu_new_vlan_client(vlan, model, name, NULL, net_socket_receive,
|
2009-04-18 01:11:08 +08:00
|
|
|
NULL, net_socket_cleanup, s);
|
2008-11-01 03:10:00 +08:00
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
|
|
|
"socket: fd=%d", fd);
|
|
|
|
if (is_connected) {
|
|
|
|
net_socket_connect(s);
|
|
|
|
} else {
|
|
|
|
qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static NetSocketState *net_socket_fd_init(VLANState *vlan,
|
|
|
|
const char *model, const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
int fd, int is_connected)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
2009-10-01 02:56:44 +08:00
|
|
|
int so_type = -1, optlen=sizeof(so_type);
|
2008-11-01 03:10:00 +08:00
|
|
|
|
|
|
|
if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
|
|
|
|
(socklen_t *)&optlen)< 0) {
|
|
|
|
fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
switch(so_type) {
|
|
|
|
case SOCK_DGRAM:
|
2009-01-08 01:48:51 +08:00
|
|
|
return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
|
2008-11-01 03:10:00 +08:00
|
|
|
case SOCK_STREAM:
|
2009-01-08 01:48:51 +08:00
|
|
|
return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
|
2008-11-01 03:10:00 +08:00
|
|
|
default:
|
|
|
|
/* who knows ... this could be a eg. a pty, do warn and continue as stream */
|
|
|
|
fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
|
2009-01-08 01:48:51 +08:00
|
|
|
return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_socket_accept(void *opaque)
|
|
|
|
{
|
|
|
|
NetSocketListenState *s = opaque;
|
|
|
|
NetSocketState *s1;
|
|
|
|
struct sockaddr_in saddr;
|
|
|
|
socklen_t len;
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
for(;;) {
|
|
|
|
len = sizeof(saddr);
|
|
|
|
fd = accept(s->fd, (struct sockaddr *)&saddr, &len);
|
|
|
|
if (fd < 0 && errno != EINTR) {
|
|
|
|
return;
|
|
|
|
} else if (fd >= 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (!s1) {
|
|
|
|
closesocket(fd);
|
|
|
|
} else {
|
|
|
|
snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
|
|
|
|
"socket: connection from %s:%d",
|
|
|
|
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static int net_socket_listen_init(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
const char *host_str)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
NetSocketListenState *s;
|
|
|
|
int fd, val, ret;
|
|
|
|
struct sockaddr_in saddr;
|
|
|
|
|
|
|
|
if (parse_host_port(&saddr, host_str) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
s = qemu_mallocz(sizeof(NetSocketListenState));
|
|
|
|
|
|
|
|
fd = socket(PF_INET, SOCK_STREAM, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
perror("socket");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
socket_set_nonblock(fd);
|
|
|
|
|
|
|
|
/* allow fast reuse */
|
|
|
|
val = 1;
|
|
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
|
|
|
|
|
|
|
|
ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("bind");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ret = listen(fd, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
perror("listen");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s->vlan = vlan;
|
2009-10-06 19:16:55 +08:00
|
|
|
s->model = qemu_strdup(model);
|
|
|
|
s->name = name ? qemu_strdup(name) : NULL;
|
2008-11-01 03:10:00 +08:00
|
|
|
s->fd = fd;
|
|
|
|
qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static int net_socket_connect_init(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
const char *host_str)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
NetSocketState *s;
|
|
|
|
int fd, connected, ret, err;
|
|
|
|
struct sockaddr_in saddr;
|
|
|
|
|
|
|
|
if (parse_host_port(&saddr, host_str) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
fd = socket(PF_INET, SOCK_STREAM, 0);
|
|
|
|
if (fd < 0) {
|
|
|
|
perror("socket");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
socket_set_nonblock(fd);
|
|
|
|
|
|
|
|
connected = 0;
|
|
|
|
for(;;) {
|
|
|
|
ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
|
|
|
|
if (ret < 0) {
|
|
|
|
err = socket_error();
|
|
|
|
if (err == EINTR || err == EWOULDBLOCK) {
|
|
|
|
} else if (err == EINPROGRESS) {
|
|
|
|
break;
|
|
|
|
#ifdef _WIN32
|
|
|
|
} else if (err == WSAEALREADY) {
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
perror("connect");
|
|
|
|
closesocket(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
connected = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
s = net_socket_fd_init(vlan, model, name, fd, connected);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (!s)
|
|
|
|
return -1;
|
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
|
|
|
"socket: connect to %s:%d",
|
|
|
|
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
static int net_socket_mcast_init(VLANState *vlan,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
2009-01-08 01:42:25 +08:00
|
|
|
const char *host_str)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
NetSocketState *s;
|
|
|
|
int fd;
|
|
|
|
struct sockaddr_in saddr;
|
|
|
|
|
|
|
|
if (parse_host_port(&saddr, host_str) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
fd = net_socket_mcast_create(&saddr);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
s = net_socket_fd_init(vlan, model, name, fd, 0);
|
2008-11-01 03:10:00 +08:00
|
|
|
if (!s)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
s->dgram_dst = saddr;
|
|
|
|
|
|
|
|
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
|
|
|
|
"socket: mcast=%s:%d",
|
|
|
|
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-04-22 03:56:28 +08:00
|
|
|
typedef struct DumpState {
|
|
|
|
VLANClientState *pcap_vc;
|
|
|
|
int fd;
|
|
|
|
int pcap_caplen;
|
|
|
|
} DumpState;
|
|
|
|
|
|
|
|
#define PCAP_MAGIC 0xa1b2c3d4
|
|
|
|
|
|
|
|
struct pcap_file_hdr {
|
|
|
|
uint32_t magic;
|
|
|
|
uint16_t version_major;
|
|
|
|
uint16_t version_minor;
|
|
|
|
int32_t thiszone;
|
|
|
|
uint32_t sigfigs;
|
|
|
|
uint32_t snaplen;
|
|
|
|
uint32_t linktype;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct pcap_sf_pkthdr {
|
|
|
|
struct {
|
|
|
|
int32_t tv_sec;
|
|
|
|
int32_t tv_usec;
|
|
|
|
} ts;
|
|
|
|
uint32_t caplen;
|
|
|
|
uint32_t len;
|
|
|
|
};
|
|
|
|
|
2009-05-18 20:40:55 +08:00
|
|
|
static ssize_t dump_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
|
2009-04-22 03:56:28 +08:00
|
|
|
{
|
2009-05-18 20:33:03 +08:00
|
|
|
DumpState *s = vc->opaque;
|
2009-04-22 03:56:28 +08:00
|
|
|
struct pcap_sf_pkthdr hdr;
|
|
|
|
int64_t ts;
|
|
|
|
int caplen;
|
|
|
|
|
|
|
|
/* Early return in case of previous error. */
|
|
|
|
if (s->fd < 0) {
|
2009-05-18 20:40:55 +08:00
|
|
|
return size;
|
2009-04-22 03:56:28 +08:00
|
|
|
}
|
|
|
|
|
2009-09-10 09:04:26 +08:00
|
|
|
ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec());
|
2009-04-22 03:56:28 +08:00
|
|
|
caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
|
|
|
|
|
2009-05-11 04:23:23 +08:00
|
|
|
hdr.ts.tv_sec = ts / 1000000;
|
2009-04-22 03:56:28 +08:00
|
|
|
hdr.ts.tv_usec = ts % 1000000;
|
|
|
|
hdr.caplen = caplen;
|
|
|
|
hdr.len = size;
|
|
|
|
if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) ||
|
|
|
|
write(s->fd, buf, caplen) != caplen) {
|
|
|
|
qemu_log("-net dump write error - stop dump\n");
|
|
|
|
close(s->fd);
|
|
|
|
s->fd = -1;
|
|
|
|
}
|
2009-05-18 20:40:55 +08:00
|
|
|
|
|
|
|
return size;
|
2009-04-22 03:56:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void net_dump_cleanup(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
DumpState *s = vc->opaque;
|
|
|
|
|
|
|
|
close(s->fd);
|
|
|
|
qemu_free(s);
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:16:58 +08:00
|
|
|
static int net_dump_init(VLANState *vlan, const char *device,
|
2009-04-22 03:56:28 +08:00
|
|
|
const char *name, const char *filename, int len)
|
|
|
|
{
|
|
|
|
struct pcap_file_hdr hdr;
|
|
|
|
DumpState *s;
|
|
|
|
|
|
|
|
s = qemu_malloc(sizeof(DumpState));
|
|
|
|
|
2009-06-18 01:48:08 +08:00
|
|
|
s->fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644);
|
2009-04-22 03:56:28 +08:00
|
|
|
if (s->fd < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("-net dump: can't open %s\n", filename);
|
2009-04-22 03:56:28 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->pcap_caplen = len;
|
|
|
|
|
|
|
|
hdr.magic = PCAP_MAGIC;
|
|
|
|
hdr.version_major = 2;
|
|
|
|
hdr.version_minor = 4;
|
|
|
|
hdr.thiszone = 0;
|
|
|
|
hdr.sigfigs = 0;
|
|
|
|
hdr.snaplen = s->pcap_caplen;
|
|
|
|
hdr.linktype = 1;
|
|
|
|
|
|
|
|
if (write(s->fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("-net dump write error: %s\n", strerror(errno));
|
2009-04-22 03:56:28 +08:00
|
|
|
close(s->fd);
|
|
|
|
qemu_free(s);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-05-18 19:55:27 +08:00
|
|
|
s->pcap_vc = qemu_new_vlan_client(vlan, device, name, NULL, dump_receive, NULL,
|
2009-04-22 03:56:28 +08:00
|
|
|
net_dump_cleanup, s);
|
|
|
|
snprintf(s->pcap_vc->info_str, sizeof(s->pcap_vc->info_str),
|
|
|
|
"dump to %s (len=%d)", filename, len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
/* find or alloc a new VLAN */
|
2009-06-24 20:42:31 +08:00
|
|
|
VLANState *qemu_find_vlan(int id, int allocate)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
VLANState **pvlan, *vlan;
|
|
|
|
for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
|
|
|
|
if (vlan->id == id)
|
|
|
|
return vlan;
|
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
if (!allocate) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
vlan = qemu_mallocz(sizeof(VLANState));
|
|
|
|
vlan->id = id;
|
2009-09-12 15:36:22 +08:00
|
|
|
QTAILQ_INIT(&vlan->send_queue);
|
2008-11-01 03:10:00 +08:00
|
|
|
vlan->next = NULL;
|
|
|
|
pvlan = &first_vlan;
|
|
|
|
while (*pvlan != NULL)
|
|
|
|
pvlan = &(*pvlan)->next;
|
|
|
|
*pvlan = vlan;
|
|
|
|
return vlan;
|
|
|
|
}
|
|
|
|
|
2009-02-11 23:20:03 +08:00
|
|
|
static int nic_get_free_idx(void)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (index = 0; index < MAX_NICS; index++)
|
|
|
|
if (!nd_table[index].used)
|
|
|
|
return index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-09-25 09:53:51 +08:00
|
|
|
int qemu_show_nic_models(const char *arg, const char *const *models)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!arg || strcmp(arg, "?"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fprintf(stderr, "qemu: Supported NIC models: ");
|
|
|
|
for (i = 0 ; models[i]; i++)
|
|
|
|
fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-01-14 03:03:57 +08:00
|
|
|
void qemu_check_nic_model(NICInfo *nd, const char *model)
|
|
|
|
{
|
|
|
|
const char *models[2];
|
|
|
|
|
|
|
|
models[0] = model;
|
|
|
|
models[1] = NULL;
|
|
|
|
|
2009-09-25 09:53:51 +08:00
|
|
|
if (qemu_show_nic_models(nd->model, models))
|
|
|
|
exit(0);
|
|
|
|
if (qemu_find_nic_model(nd, models, model) < 0)
|
|
|
|
exit(1);
|
2009-01-14 03:03:57 +08:00
|
|
|
}
|
|
|
|
|
2009-09-25 09:53:51 +08:00
|
|
|
int qemu_find_nic_model(NICInfo *nd, const char * const *models,
|
|
|
|
const char *default_model)
|
2009-01-14 03:03:57 +08:00
|
|
|
{
|
2009-09-25 09:53:51 +08:00
|
|
|
int i;
|
2009-01-14 03:03:57 +08:00
|
|
|
|
|
|
|
if (!nd->model)
|
2009-10-06 19:16:51 +08:00
|
|
|
nd->model = qemu_strdup(default_model);
|
2009-01-14 03:03:57 +08:00
|
|
|
|
2009-09-25 09:53:51 +08:00
|
|
|
for (i = 0 ; models[i]; i++) {
|
|
|
|
if (strcmp(nd->model, models[i]) == 0)
|
|
|
|
return i;
|
2009-01-14 03:03:57 +08:00
|
|
|
}
|
|
|
|
|
2009-09-25 09:53:51 +08:00
|
|
|
qemu_error("qemu: Unsupported NIC model: %s\n", nd->model);
|
|
|
|
return -1;
|
2009-01-14 03:03:57 +08:00
|
|
|
}
|
|
|
|
|
2009-07-22 16:11:42 +08:00
|
|
|
static int net_handle_fd_param(Monitor *mon, const char *param)
|
|
|
|
{
|
|
|
|
if (!qemu_isdigit(param[0])) {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
fd = monitor_get_fd(mon, param);
|
|
|
|
if (fd == -1) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("No file descriptor named %s found", param);
|
2009-07-22 16:11:42 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
} else {
|
|
|
|
return strtol(param, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:17:06 +08:00
|
|
|
static int net_init_nic(QemuOpts *opts, Monitor *mon)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
NICInfo *nd;
|
|
|
|
|
|
|
|
idx = nic_get_free_idx();
|
|
|
|
if (idx == -1 || nb_nics >= MAX_NICS) {
|
|
|
|
qemu_error("Too Many NICs\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd = &nd_table[idx];
|
|
|
|
|
|
|
|
memset(nd, 0, sizeof(*nd));
|
|
|
|
|
|
|
|
nd->vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
|
|
|
|
|
|
|
|
if (qemu_opts_id(opts)) {
|
|
|
|
nd->id = qemu_strdup(qemu_opts_id(opts));
|
|
|
|
}
|
|
|
|
if (qemu_opt_get(opts, "name")) {
|
|
|
|
nd->name = qemu_strdup(qemu_opt_get(opts, "name"));
|
|
|
|
}
|
|
|
|
if (qemu_opt_get(opts, "model")) {
|
|
|
|
nd->model = qemu_strdup(qemu_opt_get(opts, "model"));
|
|
|
|
}
|
|
|
|
if (qemu_opt_get(opts, "addr")) {
|
|
|
|
nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr"));
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->macaddr[0] = 0x52;
|
|
|
|
nd->macaddr[1] = 0x54;
|
|
|
|
nd->macaddr[2] = 0x00;
|
|
|
|
nd->macaddr[3] = 0x12;
|
|
|
|
nd->macaddr[4] = 0x34;
|
|
|
|
nd->macaddr[5] = 0x56 + idx;
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "macaddr") &&
|
|
|
|
parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
|
|
|
|
qemu_error("invalid syntax for ethernet address\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->nvectors = qemu_opt_get_number(opts, "vectors", NIC_NVECTORS_UNSPECIFIED);
|
|
|
|
if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED &&
|
|
|
|
(nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
|
|
|
|
qemu_error("invalid # of vectors: %d\n", nd->nvectors);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->used = 1;
|
|
|
|
nd->vlan->nb_guest_devs++;
|
|
|
|
nb_nics++;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:17:07 +08:00
|
|
|
static int net_init_slirp_configs(const char *name, const char *value, void *opaque)
|
|
|
|
{
|
|
|
|
struct slirp_config_str *config;
|
|
|
|
|
|
|
|
if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
config = qemu_mallocz(sizeof(*config));
|
|
|
|
|
|
|
|
pstrcpy(config->str, sizeof(config->str), value);
|
|
|
|
|
|
|
|
if (!strcmp(name, "hostfwd")) {
|
|
|
|
config->flags = SLIRP_CFG_HOSTFWD;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->next = slirp_configs;
|
|
|
|
slirp_configs = config;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_init_slirp(QemuOpts *opts, Monitor *mon)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
struct slirp_config_str *config;
|
|
|
|
const char *name;
|
|
|
|
const char *vhost;
|
|
|
|
const char *vhostname;
|
|
|
|
const char *vdhcp_start;
|
|
|
|
const char *vnamesrv;
|
|
|
|
const char *tftp_export;
|
|
|
|
const char *bootfile;
|
|
|
|
const char *smb_export;
|
|
|
|
const char *vsmbsrv;
|
|
|
|
char *vnet = NULL;
|
|
|
|
int restricted = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
|
|
|
|
|
|
|
|
name = qemu_opt_get(opts, "name");
|
|
|
|
|
|
|
|
vhost = qemu_opt_get(opts, "host");
|
|
|
|
vhostname = qemu_opt_get(opts, "hostname");
|
|
|
|
vdhcp_start = qemu_opt_get(opts, "dhcpstart");
|
|
|
|
vnamesrv = qemu_opt_get(opts, "dns");
|
|
|
|
tftp_export = qemu_opt_get(opts, "tftp");
|
|
|
|
bootfile = qemu_opt_get(opts, "bootfile");
|
|
|
|
smb_export = qemu_opt_get(opts, "smb");
|
|
|
|
vsmbsrv = qemu_opt_get(opts, "smbserver");
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "ip")) {
|
|
|
|
const char *ip = qemu_opt_get(opts, "ip");
|
|
|
|
int l = strlen(ip) + strlen("/24") + 1;
|
|
|
|
|
|
|
|
vnet = qemu_malloc(l);
|
|
|
|
|
|
|
|
/* emulate legacy ip= parameter */
|
|
|
|
pstrcpy(vnet, l, ip);
|
|
|
|
pstrcat(vnet, l, "/24");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "net")) {
|
|
|
|
if (vnet) {
|
|
|
|
qemu_free(vnet);
|
|
|
|
}
|
|
|
|
vnet = qemu_strdup(qemu_opt_get(opts, "net"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "restrict") &&
|
|
|
|
qemu_opt_get(opts, "restrict")[0] == 'y') {
|
|
|
|
restricted = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
|
|
|
|
|
|
|
|
ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
|
|
|
|
vhostname, tftp_export, bootfile, vdhcp_start,
|
|
|
|
vnamesrv, smb_export, vsmbsrv);
|
|
|
|
|
|
|
|
while (slirp_configs) {
|
|
|
|
config = slirp_configs;
|
|
|
|
slirp_configs = config->next;
|
|
|
|
qemu_free(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret != -1) {
|
|
|
|
vlan->nb_host_devs++;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_free(vnet);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-10-06 19:17:08 +08:00
|
|
|
#ifdef _WIN32
|
|
|
|
static int net_init_tap_win32(QemuOpts *opts, Monitor *mon)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
const char *name;
|
|
|
|
const char *ifname;
|
|
|
|
|
|
|
|
vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
|
|
|
|
|
|
|
|
name = qemu_opt_get(opts, "name");
|
|
|
|
ifname = qemu_opt_get(opts, "ifname");
|
|
|
|
|
|
|
|
if (!ifname) {
|
|
|
|
qemu_error("tap: no interface name\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tap_win32_init(vlan, "tap", name, ifname) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan->nb_host_devs++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#elif !defined(_AIX)
|
|
|
|
static int net_init_tap(QemuOpts *opts, Monitor *mon)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
const char *name;
|
|
|
|
TAPState *s;
|
|
|
|
|
|
|
|
vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
|
|
|
|
|
|
|
|
name = qemu_opt_get(opts, "name");
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "fd")) {
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "ifname") ||
|
|
|
|
qemu_opt_get(opts, "script") ||
|
|
|
|
qemu_opt_get(opts, "downscript")) {
|
|
|
|
qemu_error("ifname=, script= and downscript= is invalid with fd=\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
|
|
|
|
if (fd == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
|
|
|
|
s = net_tap_fd_init(vlan, "tap", name, fd);
|
|
|
|
if (!s) {
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const char *ifname, *script, *downscript;
|
|
|
|
|
|
|
|
ifname = qemu_opt_get(opts, "ifname");
|
|
|
|
script = qemu_opt_get(opts, "script");
|
|
|
|
downscript = qemu_opt_get(opts, "downscript");
|
|
|
|
|
|
|
|
if (!script) {
|
|
|
|
script = DEFAULT_NETWORK_SCRIPT;
|
|
|
|
}
|
|
|
|
if (!downscript) {
|
|
|
|
downscript = DEFAULT_NETWORK_DOWN_SCRIPT;
|
|
|
|
}
|
|
|
|
|
|
|
|
s = net_tap_init(vlan, "tap", name, ifname, script, downscript);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!s) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tap_set_sndbuf(s, opts) < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan->nb_host_devs++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-10-06 19:17:06 +08:00
|
|
|
#define NET_COMMON_PARAMS_DESC \
|
|
|
|
{ \
|
|
|
|
.name = "type", \
|
|
|
|
.type = QEMU_OPT_STRING, \
|
|
|
|
.help = "net client type (nic, tap etc.)", \
|
|
|
|
}, { \
|
|
|
|
.name = "vlan", \
|
|
|
|
.type = QEMU_OPT_NUMBER, \
|
|
|
|
.help = "vlan number", \
|
|
|
|
}, { \
|
|
|
|
.name = "name", \
|
|
|
|
.type = QEMU_OPT_STRING, \
|
|
|
|
.help = "identifier for monitor commands", \
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef int (*net_client_init_func)(QemuOpts *opts, Monitor *mon);
|
|
|
|
|
|
|
|
/* magic number, but compiler will warn if too small */
|
|
|
|
#define NET_MAX_DESC 20
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
const char *type;
|
|
|
|
net_client_init_func init;
|
|
|
|
QemuOptDesc desc[NET_MAX_DESC];
|
|
|
|
} net_client_types[] = {
|
|
|
|
{
|
|
|
|
.type = "none",
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
}, {
|
|
|
|
.type = "nic",
|
|
|
|
.init = net_init_nic,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "macaddr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "MAC address",
|
|
|
|
}, {
|
|
|
|
.name = "model",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "device model (e1000, rtl8139, virtio etc.)",
|
|
|
|
}, {
|
|
|
|
.name = "addr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "PCI device address",
|
|
|
|
}, {
|
|
|
|
.name = "vectors",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "number of MSI-x vectors, 0 to disable MSI-X",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2009-10-06 19:17:07 +08:00
|
|
|
#ifdef CONFIG_SLIRP
|
|
|
|
}, {
|
|
|
|
.type = "user",
|
|
|
|
.init = net_init_slirp,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "hostname",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "client hostname reported by the builtin DHCP server",
|
|
|
|
}, {
|
|
|
|
.name = "restrict",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "isolate the guest from the host (y|yes|n|no)",
|
|
|
|
}, {
|
|
|
|
.name = "ip",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "legacy parameter, use net= instead",
|
|
|
|
}, {
|
|
|
|
.name = "net",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address and optional netmask",
|
|
|
|
}, {
|
|
|
|
.name = "host",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest-visible address of the host",
|
|
|
|
}, {
|
|
|
|
.name = "tftp",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "root directory of the built-in TFTP server",
|
|
|
|
}, {
|
|
|
|
.name = "bootfile",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "BOOTP filename, for use with tftp=",
|
|
|
|
}, {
|
|
|
|
.name = "dhcpstart",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "the first of the 16 IPs the built-in DHCP server can assign",
|
|
|
|
}, {
|
|
|
|
.name = "dns",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest-visible address of the virtual nameserver",
|
|
|
|
}, {
|
|
|
|
.name = "smb",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "root directory of the built-in SMB server",
|
|
|
|
}, {
|
|
|
|
.name = "smbserver",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address of the built-in SMB server",
|
|
|
|
}, {
|
|
|
|
.name = "hostfwd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest port number to forward incoming TCP or UDP connections",
|
|
|
|
}, {
|
|
|
|
.name = "guestfwd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address and port to forward guest TCP connections",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2009-10-06 19:17:08 +08:00
|
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
|
|
}, {
|
|
|
|
.type = "tap",
|
|
|
|
.init = net_init_tap_win32,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "ifname",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "interface name",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
#elif !defined(_AIX)
|
|
|
|
}, {
|
|
|
|
.type = "tap",
|
|
|
|
.init = net_init_tap,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "fd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file descriptor of an already opened tap",
|
|
|
|
}, {
|
|
|
|
.name = "ifname",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "interface name",
|
|
|
|
}, {
|
|
|
|
.name = "script",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "script to initialize the interface",
|
|
|
|
}, {
|
|
|
|
.name = "downscript",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "script to shut down the interface",
|
|
|
|
#ifdef TUNSETSNDBUF
|
|
|
|
}, {
|
|
|
|
.name = "sndbuf",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
.help = "send buffer limit"
|
|
|
|
#endif
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2009-10-06 19:17:07 +08:00
|
|
|
#endif
|
2009-10-06 19:17:06 +08:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int net_client_init_from_opts(Monitor *mon, QemuOpts *opts)
|
|
|
|
{
|
|
|
|
const char *type;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
type = qemu_opt_get(opts, "type");
|
|
|
|
if (!type) {
|
|
|
|
qemu_error("No type specified for -net\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; net_client_types[i].type != NULL; i++) {
|
|
|
|
if (!strcmp(net_client_types[i].type, type)) {
|
|
|
|
if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_client_types[i].init) {
|
|
|
|
return net_client_types[i].init(opts, NULL);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_error("Invalid -net type '%s'\n", type);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-05-08 18:34:18 +08:00
|
|
|
int net_client_init(Monitor *mon, const char *device, const char *p)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
int vlan_id, ret;
|
|
|
|
VLANState *vlan;
|
2009-01-08 01:48:51 +08:00
|
|
|
char *name = NULL;
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-10-06 19:17:06 +08:00
|
|
|
if (!strcmp(device, "none") ||
|
2009-10-06 19:17:07 +08:00
|
|
|
!strcmp(device, "nic") ||
|
2009-10-06 19:17:08 +08:00
|
|
|
!strcmp(device, "user") ||
|
|
|
|
!strcmp(device, "tap")) {
|
2009-10-06 19:17:06 +08:00
|
|
|
QemuOpts *opts;
|
|
|
|
|
|
|
|
opts = qemu_opts_parse(&qemu_net_opts, p, NULL);
|
|
|
|
if (!opts) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_opt_set(opts, "type", device);
|
|
|
|
|
|
|
|
return net_client_init_from_opts(mon, opts);
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
vlan_id = 0;
|
|
|
|
if (get_param_value(buf, sizeof(buf), "vlan", p)) {
|
|
|
|
vlan_id = strtol(buf, NULL, 0);
|
|
|
|
}
|
2009-06-24 20:42:31 +08:00
|
|
|
vlan = qemu_find_vlan(vlan_id, 1);
|
2009-04-18 01:10:51 +08:00
|
|
|
|
2009-01-08 01:48:51 +08:00
|
|
|
if (get_param_value(buf, sizeof(buf), "name", p)) {
|
2009-05-08 18:34:18 +08:00
|
|
|
name = qemu_strdup(buf);
|
2009-01-08 01:48:51 +08:00
|
|
|
}
|
2009-10-06 19:17:06 +08:00
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
#ifdef CONFIG_SLIRP
|
2009-10-06 19:17:07 +08:00
|
|
|
if (!strcmp(device, "channel")) {
|
2009-09-12 15:36:22 +08:00
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
2009-06-24 20:42:28 +08:00
|
|
|
struct slirp_config_str *config;
|
|
|
|
|
|
|
|
config = qemu_malloc(sizeof(*config));
|
|
|
|
pstrcpy(config->str, sizeof(config->str), p);
|
2009-06-24 20:42:28 +08:00
|
|
|
config->flags = SLIRP_CFG_LEGACY;
|
2009-06-24 20:42:28 +08:00
|
|
|
config->next = slirp_configs;
|
|
|
|
slirp_configs = config;
|
Don't exit() in config_error()
Propagating errors up the call chain is tedious. In startup code, we
can take a shortcut: terminate the program. This is wrong elsewhere,
the monitor in particular.
config_error() tries to cater for both customers: it terminates the
program unless its mon parameter tells it it's working for the
monitor.
Its users need to return status anyway (unless passing a null mon
argument, which none do), which their users need to check. So this
automatic exit buys us exactly nothing useful. Only the dangerous
delusion that we can get away without returning status. Some of its
users fell for that. Their callers continue executing after failure
when working for the monitor.
This bites monitor command host_net_add in two places:
* net_slirp_init() continues after slirp_hostfwd(), slirp_guestfwd(),
or slirp_smb() failed, and may end up reporting success. This
happens for "host_net_add user guestfwd=foo": it complains about the
invalid guest forwarding rule, then happily creates the user network
without guest forwarding.
* net_client_init() can't detect slirp_guestfwd() failure, and gets
fooled by net_slirp_init() lying about success. Suppresses its
"Could not initialize device" message.
Add the missing error reporting, make sure errors are checked, and
drop the exit() from config_error().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2009-10-06 19:16:57 +08:00
|
|
|
ret = 0;
|
2009-06-24 20:42:28 +08:00
|
|
|
} else {
|
2009-10-06 19:16:58 +08:00
|
|
|
ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), p, 1);
|
2009-02-16 23:34:18 +08:00
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (!strcmp(device, "socket")) {
|
2009-05-08 18:34:17 +08:00
|
|
|
char chkbuf[64];
|
2008-11-01 03:10:00 +08:00
|
|
|
if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
|
2009-06-19 01:21:34 +08:00
|
|
|
static const char * const fd_params[] = {
|
|
|
|
"vlan", "name", "fd", NULL
|
|
|
|
};
|
2008-11-01 03:10:00 +08:00
|
|
|
int fd;
|
2009-07-22 16:11:42 +08:00
|
|
|
ret = -1;
|
2009-05-08 18:34:17 +08:00
|
|
|
if (check_params(chkbuf, sizeof(chkbuf), fd_params, p) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid parameter '%s' in '%s'\n", chkbuf, p);
|
2009-05-08 18:34:18 +08:00
|
|
|
goto out;
|
2009-04-22 03:56:15 +08:00
|
|
|
}
|
2009-07-22 16:11:42 +08:00
|
|
|
fd = net_handle_fd_param(mon, buf);
|
|
|
|
if (fd == -1) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!net_socket_fd_init(vlan, device, name, fd, 1)) {
|
|
|
|
close(fd);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
2008-11-01 03:10:00 +08:00
|
|
|
} else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) {
|
2009-04-22 03:56:15 +08:00
|
|
|
static const char * const listen_params[] = {
|
|
|
|
"vlan", "name", "listen", NULL
|
|
|
|
};
|
2009-05-08 18:34:17 +08:00
|
|
|
if (check_params(chkbuf, sizeof(chkbuf), listen_params, p) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid parameter '%s' in '%s'\n", chkbuf, p);
|
2009-05-08 18:34:18 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2009-04-22 03:56:15 +08:00
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
ret = net_socket_listen_init(vlan, device, name, buf);
|
2008-11-01 03:10:00 +08:00
|
|
|
} else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
|
2009-04-22 03:56:15 +08:00
|
|
|
static const char * const connect_params[] = {
|
|
|
|
"vlan", "name", "connect", NULL
|
|
|
|
};
|
2009-05-08 18:34:17 +08:00
|
|
|
if (check_params(chkbuf, sizeof(chkbuf), connect_params, p) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid parameter '%s' in '%s'\n", chkbuf, p);
|
2009-05-08 18:34:18 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2009-04-22 03:56:15 +08:00
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
ret = net_socket_connect_init(vlan, device, name, buf);
|
2008-11-01 03:10:00 +08:00
|
|
|
} else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) {
|
2009-04-22 03:56:15 +08:00
|
|
|
static const char * const mcast_params[] = {
|
|
|
|
"vlan", "name", "mcast", NULL
|
|
|
|
};
|
2009-05-08 18:34:17 +08:00
|
|
|
if (check_params(chkbuf, sizeof(chkbuf), mcast_params, p) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid parameter '%s' in '%s'\n", chkbuf, p);
|
2009-05-08 18:34:18 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2009-04-22 03:56:15 +08:00
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
ret = net_socket_mcast_init(vlan, device, name, buf);
|
2008-11-01 03:10:00 +08:00
|
|
|
} else {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("Unknown socket options: %s\n", p);
|
2009-04-18 01:10:43 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
vlan->nb_host_devs++;
|
|
|
|
} else
|
|
|
|
#ifdef CONFIG_VDE
|
|
|
|
if (!strcmp(device, "vde")) {
|
2009-04-22 03:56:15 +08:00
|
|
|
static const char * const vde_params[] = {
|
|
|
|
"vlan", "name", "sock", "port", "group", "mode", NULL
|
|
|
|
};
|
2008-11-01 03:10:00 +08:00
|
|
|
char vde_sock[1024], vde_group[512];
|
|
|
|
int vde_port, vde_mode;
|
2009-04-22 03:56:15 +08:00
|
|
|
|
2009-05-08 18:34:17 +08:00
|
|
|
if (check_params(buf, sizeof(buf), vde_params, p) < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("invalid parameter '%s' in '%s'\n", buf, p);
|
2009-05-08 18:34:18 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2009-04-22 03:56:15 +08:00
|
|
|
}
|
2008-11-01 03:10:00 +08:00
|
|
|
vlan->nb_host_devs++;
|
|
|
|
if (get_param_value(vde_sock, sizeof(vde_sock), "sock", p) <= 0) {
|
|
|
|
vde_sock[0] = '\0';
|
|
|
|
}
|
|
|
|
if (get_param_value(buf, sizeof(buf), "port", p) > 0) {
|
|
|
|
vde_port = strtol(buf, NULL, 10);
|
|
|
|
} else {
|
|
|
|
vde_port = 0;
|
|
|
|
}
|
|
|
|
if (get_param_value(vde_group, sizeof(vde_group), "group", p) <= 0) {
|
|
|
|
vde_group[0] = '\0';
|
|
|
|
}
|
|
|
|
if (get_param_value(buf, sizeof(buf), "mode", p) > 0) {
|
|
|
|
vde_mode = strtol(buf, NULL, 8);
|
|
|
|
} else {
|
|
|
|
vde_mode = 0700;
|
|
|
|
}
|
2009-01-08 01:48:51 +08:00
|
|
|
ret = net_vde_init(vlan, device, name, vde_sock, vde_port, vde_group, vde_mode);
|
2008-11-01 03:10:00 +08:00
|
|
|
} else
|
|
|
|
#endif
|
2009-04-22 03:56:28 +08:00
|
|
|
if (!strcmp(device, "dump")) {
|
|
|
|
int len = 65536;
|
|
|
|
|
|
|
|
if (get_param_value(buf, sizeof(buf), "len", p) > 0) {
|
|
|
|
len = strtol(buf, NULL, 0);
|
|
|
|
}
|
|
|
|
if (!get_param_value(buf, sizeof(buf), "file", p)) {
|
|
|
|
snprintf(buf, sizeof(buf), "qemu-vlan%d.pcap", vlan_id);
|
|
|
|
}
|
2009-10-06 19:16:58 +08:00
|
|
|
ret = net_dump_init(vlan, device, name, buf, len);
|
2009-04-22 03:56:28 +08:00
|
|
|
} else {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("Unknown network device: %s\n", device);
|
2009-04-18 01:10:43 +08:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
if (ret < 0) {
|
2009-10-06 19:16:58 +08:00
|
|
|
qemu_error("Could not initialize device '%s'\n", device);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
2009-04-18 01:10:43 +08:00
|
|
|
out:
|
2009-05-08 18:34:18 +08:00
|
|
|
qemu_free(name);
|
2008-11-01 03:10:00 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-02-11 23:20:51 +08:00
|
|
|
void net_client_uninit(NICInfo *nd)
|
|
|
|
{
|
|
|
|
nd->vlan->nb_guest_devs--;
|
|
|
|
nb_nics--;
|
2009-09-18 04:53:39 +08:00
|
|
|
|
2009-10-06 19:16:53 +08:00
|
|
|
qemu_free(nd->model);
|
|
|
|
qemu_free(nd->name);
|
|
|
|
qemu_free(nd->devaddr);
|
|
|
|
qemu_free(nd->id);
|
2009-09-18 04:53:39 +08:00
|
|
|
|
2009-10-06 19:16:54 +08:00
|
|
|
nd->used = 0;
|
2009-02-11 23:20:51 +08:00
|
|
|
}
|
|
|
|
|
2009-02-11 23:21:54 +08:00
|
|
|
static int net_host_check_device(const char *device)
|
|
|
|
{
|
|
|
|
int i;
|
2009-04-22 03:56:28 +08:00
|
|
|
const char *valid_param_list[] = { "tap", "socket", "dump"
|
2009-02-11 23:21:54 +08:00
|
|
|
#ifdef CONFIG_SLIRP
|
|
|
|
,"user"
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_VDE
|
|
|
|
,"vde"
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
|
|
|
|
if (!strncmp(valid_param_list[i], device,
|
|
|
|
strlen(valid_param_list[i])))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
void net_host_device_add(Monitor *mon, const QDict *qdict)
|
2009-02-11 23:21:54 +08:00
|
|
|
{
|
2009-08-29 02:27:14 +08:00
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
|
|
|
const char *opts = qdict_get_try_str(qdict, "opts");
|
|
|
|
|
2009-02-11 23:21:54 +08:00
|
|
|
if (!net_host_check_device(device)) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "invalid host network device %s\n", device);
|
2009-02-11 23:21:54 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-05-08 18:34:18 +08:00
|
|
|
if (net_client_init(mon, device, opts ? opts : "") < 0) {
|
2009-04-22 03:56:32 +08:00
|
|
|
monitor_printf(mon, "adding host network device %s failed\n", device);
|
|
|
|
}
|
2009-02-11 23:21:54 +08:00
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
void net_host_device_remove(Monitor *mon, const QDict *qdict)
|
2009-02-11 23:21:54 +08:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
2009-08-29 02:27:14 +08:00
|
|
|
int vlan_id = qdict_get_int(qdict, "vlan_id");
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2009-02-11 23:21:54 +08:00
|
|
|
|
2009-06-24 20:42:31 +08:00
|
|
|
vc = qemu_find_vlan_client_by_name(mon, vlan_id, device);
|
2009-02-11 23:21:54 +08:00
|
|
|
if (!vc) {
|
|
|
|
return;
|
|
|
|
}
|
2009-04-22 03:56:08 +08:00
|
|
|
if (!net_host_check_device(vc->model)) {
|
|
|
|
monitor_printf(mon, "invalid host network device %s\n", device);
|
|
|
|
return;
|
|
|
|
}
|
2009-02-11 23:21:54 +08:00
|
|
|
qemu_del_vlan_client(vc);
|
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
int net_client_parse(const char *str)
|
|
|
|
{
|
|
|
|
const char *p;
|
|
|
|
char *q;
|
|
|
|
char device[64];
|
|
|
|
|
|
|
|
p = str;
|
|
|
|
q = device;
|
|
|
|
while (*p != '\0' && *p != ',') {
|
|
|
|
if ((q - device) < sizeof(device) - 1)
|
|
|
|
*q++ = *p;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
*q = '\0';
|
|
|
|
if (*p == ',')
|
|
|
|
p++;
|
|
|
|
|
2009-05-08 18:34:18 +08:00
|
|
|
return net_client_init(NULL, device, p);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
|
2009-06-17 21:05:30 +08:00
|
|
|
void net_set_boot_mask(int net_boot_mask)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Only the first four NICs may be bootable */
|
|
|
|
net_boot_mask = net_boot_mask & 0xF;
|
|
|
|
|
|
|
|
for (i = 0; i < nb_nics; i++) {
|
|
|
|
if (net_boot_mask & (1 << i)) {
|
|
|
|
nd_table[i].bootable = 1;
|
|
|
|
net_boot_mask &= ~(1 << i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (net_boot_mask) {
|
|
|
|
fprintf(stderr, "Cannot boot from non-existent NIC\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-06 07:01:23 +08:00
|
|
|
void do_info_network(Monitor *mon)
|
2008-11-01 03:10:00 +08:00
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
|
2008-11-01 03:10:00 +08:00
|
|
|
for(vc = vlan->first_client; vc != NULL; vc = vc->next)
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str);
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 02:27:14 +08:00
|
|
|
void do_set_link(Monitor *mon, const QDict *qdict)
|
2009-01-09 03:44:06 +08:00
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
VLANClientState *vc = NULL;
|
2009-08-29 02:27:14 +08:00
|
|
|
const char *name = qdict_get_str(qdict, "name");
|
|
|
|
const char *up_or_down = qdict_get_str(qdict, "up_or_down");
|
2009-01-09 03:44:06 +08:00
|
|
|
|
|
|
|
for (vlan = first_vlan; vlan != NULL; vlan = vlan->next)
|
|
|
|
for (vc = vlan->first_client; vc != NULL; vc = vc->next)
|
|
|
|
if (strcmp(vc->name, name) == 0)
|
2009-01-09 08:48:28 +08:00
|
|
|
goto done;
|
|
|
|
done:
|
2009-01-09 03:44:06 +08:00
|
|
|
|
|
|
|
if (!vc) {
|
2009-08-09 05:33:26 +08:00
|
|
|
monitor_printf(mon, "could not find network device '%s'\n", name);
|
2009-08-04 00:56:59 +08:00
|
|
|
return;
|
2009-01-09 03:44:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(up_or_down, "up") == 0)
|
|
|
|
vc->link_down = 0;
|
|
|
|
else if (strcmp(up_or_down, "down") == 0)
|
|
|
|
vc->link_down = 1;
|
|
|
|
else
|
2009-03-06 07:01:23 +08:00
|
|
|
monitor_printf(mon, "invalid link status '%s'; only 'up' or 'down' "
|
|
|
|
"valid\n", up_or_down);
|
2009-01-09 03:44:06 +08:00
|
|
|
|
2009-01-09 03:45:03 +08:00
|
|
|
if (vc->link_status_changed)
|
|
|
|
vc->link_status_changed(vc);
|
2009-01-09 03:44:06 +08:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:10:00 +08:00
|
|
|
void net_cleanup(void)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
|
|
|
|
/* close network clients */
|
|
|
|
for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
|
2009-04-18 01:11:08 +08:00
|
|
|
VLANClientState *vc = vlan->first_client;
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
while (vc) {
|
|
|
|
VLANClientState *next = vc->next;
|
2008-11-01 03:10:00 +08:00
|
|
|
|
2009-04-18 01:11:08 +08:00
|
|
|
qemu_del_vlan_client(vc);
|
|
|
|
|
|
|
|
vc = next;
|
2008-11-01 03:10:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_client_check(void)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
|
|
|
|
for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
|
|
|
|
if (vlan->nb_guest_devs == 0 && vlan->nb_host_devs == 0)
|
|
|
|
continue;
|
|
|
|
if (vlan->nb_guest_devs == 0)
|
|
|
|
fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
|
|
|
|
if (vlan->nb_host_devs == 0)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Warning: vlan %d is not connected to host network\n",
|
|
|
|
vlan->id);
|
|
|
|
}
|
|
|
|
}
|