This is a mostly-mechanical conversion that creates a new flat union 'Netdev' QAPI type that covers all the branches of the former 'NetClientOptions' simple union, where the branches are now listed in a new 'NetClientDriver' enum rather than generated from the simple union. The existence of a flat union has no change to the command line syntax accepted for new code, and will make it possible for a future patch to switch the QMP command to parse a boxed union for no change to valid QMP; but it does have some ripple effect on the C code when dealing with the new types. While making the conversion, note that the 'NetLegacy' type remains unchanged: it applies only to legacy command line options, and will not be ported to QMP, so it should remain a wrapper around a simple union; to avoid confusion, the type named 'NetClientOptions' is now gone, and we introduce 'NetLegacyOptions' in its place. Then, in the C code, we convert from NetLegacy to Netdev as soon as possible, so that the bulk of the net stack only has to deal with one QAPI type, not two. Note that since the old legacy code always rejected 'hubport', we can just omit that branch from the new 'NetLegacyOptions' simple union. Based on an idea originally by Zoltán Kővágó <DirtY.iCE.hu@gmail.com>: Message-Id: <01a527fbf1a5de880091f98cf011616a78adeeee.1441627176.git.DirtY.iCE.hu@gmail.com> although the sed script in that patch no longer applies due to other changes in the tree since then, and I also did some manual cleanups (such as fixing whitespace to keep checkpatch happy). Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1468468228-27827-13-git-send-email-eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Fixup from Eric squashed in] Signed-off-by: Markus Armbruster <armbru@redhat.com>
914 lines
25 KiB
C
914 lines
25 KiB
C
/*
|
|
* 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 "qemu/osdep.h"
|
|
#include "net/slirp.h"
|
|
|
|
|
|
#ifndef _WIN32
|
|
#include <pwd.h>
|
|
#include <sys/wait.h>
|
|
#endif
|
|
#include "net/net.h"
|
|
#include "clients.h"
|
|
#include "hub.h"
|
|
#include "monitor/monitor.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/sockets.h"
|
|
#include "slirp/libslirp.h"
|
|
#include "slirp/ip6.h"
|
|
#include "sysemu/char.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "qemu/cutils.h"
|
|
|
|
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;
|
|
}
|
|
|
|
/* slirp network adapter */
|
|
|
|
#define SLIRP_CFG_HOSTFWD 1
|
|
#define SLIRP_CFG_LEGACY 2
|
|
|
|
struct slirp_config_str {
|
|
struct slirp_config_str *next;
|
|
int flags;
|
|
char str[1024];
|
|
int legacy_format;
|
|
};
|
|
|
|
typedef struct SlirpState {
|
|
NetClientState nc;
|
|
QTAILQ_ENTRY(SlirpState) entry;
|
|
Slirp *slirp;
|
|
Notifier exit_notifier;
|
|
#ifndef _WIN32
|
|
char smb_dir[128];
|
|
#endif
|
|
} SlirpState;
|
|
|
|
static struct slirp_config_str *slirp_configs;
|
|
const char *legacy_tftp_prefix;
|
|
const char *legacy_bootp_filename;
|
|
static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
|
|
QTAILQ_HEAD_INITIALIZER(slirp_stacks);
|
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str,
|
|
int legacy_format);
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str,
|
|
int legacy_format);
|
|
|
|
#ifndef _WIN32
|
|
static const char *legacy_smb_export;
|
|
|
|
static int slirp_smb(SlirpState *s, const char *exported_dir,
|
|
struct in_addr vserver_addr);
|
|
static void slirp_smb_cleanup(SlirpState *s);
|
|
#else
|
|
static inline void slirp_smb_cleanup(SlirpState *s) { }
|
|
#endif
|
|
|
|
void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
|
|
{
|
|
SlirpState *s = opaque;
|
|
|
|
qemu_send_packet(&s->nc, pkt, pkt_len);
|
|
}
|
|
|
|
static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
|
{
|
|
SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
|
|
|
|
slirp_input(s->slirp, buf, size);
|
|
|
|
return size;
|
|
}
|
|
|
|
static void slirp_smb_exit(Notifier *n, void *data)
|
|
{
|
|
SlirpState *s = container_of(n, SlirpState, exit_notifier);
|
|
slirp_smb_cleanup(s);
|
|
}
|
|
|
|
static void net_slirp_cleanup(NetClientState *nc)
|
|
{
|
|
SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
|
|
|
|
slirp_cleanup(s->slirp);
|
|
qemu_remove_exit_notifier(&s->exit_notifier);
|
|
slirp_smb_cleanup(s);
|
|
QTAILQ_REMOVE(&slirp_stacks, s, entry);
|
|
}
|
|
|
|
static NetClientInfo net_slirp_info = {
|
|
.type = NET_CLIENT_DRIVER_USER,
|
|
.size = sizeof(SlirpState),
|
|
.receive = net_slirp_receive,
|
|
.cleanup = net_slirp_cleanup,
|
|
};
|
|
|
|
static int net_slirp_init(NetClientState *peer, const char *model,
|
|
const char *name, int restricted,
|
|
bool ipv4, const char *vnetwork, const char *vhost,
|
|
bool ipv6, const char *vprefix6, int vprefix6_len,
|
|
const char *vhost6,
|
|
const char *vhostname, const char *tftp_export,
|
|
const char *bootfile, const char *vdhcp_start,
|
|
const char *vnameserver, const char *vnameserver6,
|
|
const char *smb_export, const char *vsmbserver,
|
|
const char **dnssearch)
|
|
{
|
|
/* default settings according to historic slirp */
|
|
struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
|
|
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
|
|
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 */
|
|
struct in6_addr ip6_prefix;
|
|
struct in6_addr ip6_host;
|
|
struct in6_addr ip6_dns;
|
|
#ifndef _WIN32
|
|
struct in_addr smbsrv = { .s_addr = 0 };
|
|
#endif
|
|
NetClientState *nc;
|
|
SlirpState *s;
|
|
char buf[20];
|
|
uint32_t addr;
|
|
int shift;
|
|
char *end;
|
|
struct slirp_config_str *config;
|
|
|
|
if (!ipv4 && (vnetwork || vhost || vnameserver)) {
|
|
return -1;
|
|
}
|
|
|
|
if (!ipv6 && (vprefix6 || vhost6 || vnameserver6)) {
|
|
return -1;
|
|
}
|
|
|
|
if (!ipv4 && !ipv6) {
|
|
/* It doesn't make sense to disable both */
|
|
return -1;
|
|
}
|
|
|
|
if (!tftp_export) {
|
|
tftp_export = legacy_tftp_prefix;
|
|
}
|
|
if (!bootfile) {
|
|
bootfile = legacy_bootp_filename;
|
|
}
|
|
|
|
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 */
|
|
} else {
|
|
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)) {
|
|
return -1;
|
|
}
|
|
} else if (shift < 4 || shift > 32) {
|
|
return -1;
|
|
} else {
|
|
mask.s_addr = htonl(0xffffffff << (32 - shift));
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
if (vhost && !inet_aton(vhost, &host)) {
|
|
return -1;
|
|
}
|
|
if ((host.s_addr & mask.s_addr) != net.s_addr) {
|
|
return -1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
|
|
/* No inet_pton helper before Vista... */
|
|
if (vprefix6) {
|
|
/* Unsupported */
|
|
return -1;
|
|
}
|
|
memset(&ip6_prefix, 0, sizeof(ip6_prefix));
|
|
ip6_prefix.s6_addr[0] = 0xfe;
|
|
ip6_prefix.s6_addr[1] = 0xc0;
|
|
#else
|
|
if (!vprefix6) {
|
|
vprefix6 = "fec0::";
|
|
}
|
|
if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
if (!vprefix6_len) {
|
|
vprefix6_len = 64;
|
|
}
|
|
if (vprefix6_len < 0 || vprefix6_len > 126) {
|
|
return -1;
|
|
}
|
|
|
|
if (vhost6) {
|
|
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
|
|
return -1;
|
|
#else
|
|
if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
|
|
return -1;
|
|
}
|
|
if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
} else {
|
|
ip6_host = ip6_prefix;
|
|
ip6_host.s6_addr[15] |= 2;
|
|
}
|
|
|
|
if (vnameserver6) {
|
|
#if defined(_WIN32) && (_WIN32_WINNT < 0x0600)
|
|
return -1;
|
|
#else
|
|
if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
|
|
return -1;
|
|
}
|
|
if (!in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
|
|
return -1;
|
|
}
|
|
#endif
|
|
} else {
|
|
ip6_dns = ip6_prefix;
|
|
ip6_dns.s6_addr[15] |= 3;
|
|
}
|
|
|
|
|
|
nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
|
|
|
|
snprintf(nc->info_str, sizeof(nc->info_str),
|
|
"net=%s,restrict=%s", inet_ntoa(net),
|
|
restricted ? "on" : "off");
|
|
|
|
s = DO_UPCAST(SlirpState, nc, nc);
|
|
|
|
s->slirp = slirp_init(restricted, ipv4, net, mask, host,
|
|
ipv6, ip6_prefix, vprefix6_len, ip6_host,
|
|
vhostname, tftp_export, bootfile, dhcp,
|
|
dns, ip6_dns, dnssearch, s);
|
|
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
|
|
|
|
for (config = slirp_configs; config; config = config->next) {
|
|
if (config->flags & SLIRP_CFG_HOSTFWD) {
|
|
if (slirp_hostfwd(s, config->str,
|
|
config->flags & SLIRP_CFG_LEGACY) < 0)
|
|
goto error;
|
|
} else {
|
|
if (slirp_guestfwd(s, config->str,
|
|
config->flags & SLIRP_CFG_LEGACY) < 0)
|
|
goto error;
|
|
}
|
|
}
|
|
#ifndef _WIN32
|
|
if (!smb_export) {
|
|
smb_export = legacy_smb_export;
|
|
}
|
|
if (smb_export) {
|
|
if (slirp_smb(s, smb_export, smbsrv) < 0)
|
|
goto error;
|
|
}
|
|
#endif
|
|
|
|
s->exit_notifier.notify = slirp_smb_exit;
|
|
qemu_add_exit_notifier(&s->exit_notifier);
|
|
return 0;
|
|
|
|
error:
|
|
qemu_del_net_client(nc);
|
|
return -1;
|
|
}
|
|
|
|
static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
|
|
const char *stack)
|
|
{
|
|
|
|
if (vlan) {
|
|
NetClientState *nc;
|
|
nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
|
|
if (!nc) {
|
|
monitor_printf(mon, "unrecognized (vlan-id, stackname) pair\n");
|
|
return NULL;
|
|
}
|
|
if (strcmp(nc->model, "user")) {
|
|
monitor_printf(mon, "invalid device specified\n");
|
|
return NULL;
|
|
}
|
|
return DO_UPCAST(SlirpState, nc, nc);
|
|
} else {
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
|
monitor_printf(mon, "user mode network stack not in use\n");
|
|
return NULL;
|
|
}
|
|
return QTAILQ_FIRST(&slirp_stacks);
|
|
}
|
|
}
|
|
|
|
void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
|
|
{
|
|
struct in_addr host_addr = { .s_addr = INADDR_ANY };
|
|
int host_port;
|
|
char buf[256];
|
|
const char *src_str, *p;
|
|
SlirpState *s;
|
|
int is_udp = 0;
|
|
int err;
|
|
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");
|
|
|
|
if (arg2) {
|
|
s = slirp_lookup(mon, arg1, arg2);
|
|
src_str = arg3;
|
|
} else {
|
|
s = slirp_lookup(mon, NULL, NULL);
|
|
src_str = arg1;
|
|
}
|
|
if (!s) {
|
|
return;
|
|
}
|
|
|
|
p = src_str;
|
|
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
|
|
is_udp = 0;
|
|
} else if (!strcmp(buf, "udp")) {
|
|
is_udp = 1;
|
|
} else {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
goto fail_syntax;
|
|
}
|
|
if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
host_port = atoi(p);
|
|
|
|
err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port);
|
|
|
|
monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
|
|
err ? "not found" : "removed");
|
|
return;
|
|
|
|
fail_syntax:
|
|
monitor_printf(mon, "invalid format\n");
|
|
}
|
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str,
|
|
int legacy_format)
|
|
{
|
|
struct in_addr host_addr = { .s_addr = INADDR_ANY };
|
|
struct in_addr guest_addr = { .s_addr = 0 };
|
|
int host_port, guest_port;
|
|
const char *p;
|
|
char buf[256];
|
|
int is_udp;
|
|
char *end;
|
|
|
|
p = redir_str;
|
|
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
goto fail_syntax;
|
|
}
|
|
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
|
|
is_udp = 0;
|
|
} else if (!strcmp(buf, "udp")) {
|
|
is_udp = 1;
|
|
} else {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
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) {
|
|
goto fail_syntax;
|
|
}
|
|
host_port = strtol(buf, &end, 0);
|
|
if (*end != '\0' || host_port < 1 || host_port > 65535) {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
goto fail_syntax;
|
|
}
|
|
if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
guest_port = strtol(p, &end, 0);
|
|
if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
|
|
goto fail_syntax;
|
|
}
|
|
|
|
if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
|
|
guest_port) < 0) {
|
|
error_report("could not set up host forwarding rule '%s'",
|
|
redir_str);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
fail_syntax:
|
|
error_report("invalid host forwarding rule '%s'", redir_str);
|
|
return -1;
|
|
}
|
|
|
|
void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
|
|
{
|
|
const char *redir_str;
|
|
SlirpState *s;
|
|
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");
|
|
|
|
if (arg2) {
|
|
s = slirp_lookup(mon, arg1, arg2);
|
|
redir_str = arg3;
|
|
} else {
|
|
s = slirp_lookup(mon, NULL, NULL);
|
|
redir_str = arg1;
|
|
}
|
|
if (s) {
|
|
slirp_hostfwd(s, redir_str, 0);
|
|
}
|
|
|
|
}
|
|
|
|
int net_slirp_redir(const char *redir_str)
|
|
{
|
|
struct slirp_config_str *config;
|
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
|
config = g_malloc(sizeof(*config));
|
|
pstrcpy(config->str, sizeof(config->str), redir_str);
|
|
config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
|
|
config->next = slirp_configs;
|
|
slirp_configs = config;
|
|
return 0;
|
|
}
|
|
|
|
return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
/* automatic user mode samba server configuration */
|
|
static void slirp_smb_cleanup(SlirpState *s)
|
|
{
|
|
char cmd[128];
|
|
int ret;
|
|
|
|
if (s->smb_dir[0] != '\0') {
|
|
snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
|
|
ret = system(cmd);
|
|
if (ret == -1 || !WIFEXITED(ret)) {
|
|
error_report("'%s' failed.", cmd);
|
|
} else if (WEXITSTATUS(ret)) {
|
|
error_report("'%s' failed. Error code: %d",
|
|
cmd, WEXITSTATUS(ret));
|
|
}
|
|
s->smb_dir[0] = '\0';
|
|
}
|
|
}
|
|
|
|
static int slirp_smb(SlirpState* s, const char *exported_dir,
|
|
struct in_addr vserver_addr)
|
|
{
|
|
char smb_conf[128];
|
|
char smb_cmdline[128];
|
|
struct passwd *passwd;
|
|
FILE *f;
|
|
|
|
passwd = getpwuid(geteuid());
|
|
if (!passwd) {
|
|
error_report("failed to retrieve user name");
|
|
return -1;
|
|
}
|
|
|
|
if (access(CONFIG_SMBD_COMMAND, F_OK)) {
|
|
error_report("could not find '%s', please install it",
|
|
CONFIG_SMBD_COMMAND);
|
|
return -1;
|
|
}
|
|
|
|
if (access(exported_dir, R_OK | X_OK)) {
|
|
error_report("error accessing shared directory '%s': %s",
|
|
exported_dir, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.XXXXXX");
|
|
if (!mkdtemp(s->smb_dir)) {
|
|
error_report("could not create samba server dir '%s'", s->smb_dir);
|
|
s->smb_dir[0] = 0;
|
|
return -1;
|
|
}
|
|
snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
|
|
|
|
f = fopen(smb_conf, "w");
|
|
if (!f) {
|
|
slirp_smb_cleanup(s);
|
|
error_report("could not create samba server configuration file '%s'",
|
|
smb_conf);
|
|
return -1;
|
|
}
|
|
fprintf(f,
|
|
"[global]\n"
|
|
"private dir=%s\n"
|
|
"interfaces=127.0.0.1\n"
|
|
"bind interfaces only=yes\n"
|
|
"pid directory=%s\n"
|
|
"lock directory=%s\n"
|
|
"state directory=%s\n"
|
|
"cache directory=%s\n"
|
|
"ncalrpc dir=%s/ncalrpc\n"
|
|
"log file=%s/log.smbd\n"
|
|
"smb passwd file=%s/smbpasswd\n"
|
|
"security = user\n"
|
|
"map to guest = Bad User\n"
|
|
"load printers = no\n"
|
|
"printing = bsd\n"
|
|
"disable spoolss = yes\n"
|
|
"usershare max shares = 0\n"
|
|
"[qemu]\n"
|
|
"path=%s\n"
|
|
"read only=no\n"
|
|
"guest ok=yes\n"
|
|
"force user=%s\n",
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
s->smb_dir,
|
|
exported_dir,
|
|
passwd->pw_name
|
|
);
|
|
fclose(f);
|
|
|
|
snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -l %s -s %s",
|
|
CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);
|
|
|
|
if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 ||
|
|
slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) {
|
|
slirp_smb_cleanup(s);
|
|
error_report("conflicting/invalid smbserver address");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* automatic user mode samba server configuration (legacy interface) */
|
|
int net_slirp_smb(const char *exported_dir)
|
|
{
|
|
struct in_addr vserver_addr = { .s_addr = 0 };
|
|
|
|
if (legacy_smb_export) {
|
|
fprintf(stderr, "-smb given twice\n");
|
|
return -1;
|
|
}
|
|
legacy_smb_export = exported_dir;
|
|
if (!QTAILQ_EMPTY(&slirp_stacks)) {
|
|
return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
|
|
vserver_addr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* !defined(_WIN32) */
|
|
|
|
struct GuestFwd {
|
|
CharDriverState *hd;
|
|
struct in_addr server;
|
|
int port;
|
|
Slirp *slirp;
|
|
};
|
|
|
|
static int guestfwd_can_read(void *opaque)
|
|
{
|
|
struct GuestFwd *fwd = opaque;
|
|
return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
|
|
}
|
|
|
|
static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
|
|
{
|
|
struct GuestFwd *fwd = opaque;
|
|
slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
|
|
}
|
|
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str,
|
|
int legacy_format)
|
|
{
|
|
struct in_addr server = { .s_addr = 0 };
|
|
struct GuestFwd *fwd;
|
|
const char *p;
|
|
char buf[128];
|
|
char *end;
|
|
int port;
|
|
|
|
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;
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
|
|
|
|
if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) {
|
|
if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) {
|
|
error_report("conflicting/invalid host:port in guest forwarding "
|
|
"rule '%s'", config_str);
|
|
return -1;
|
|
}
|
|
} else {
|
|
fwd = g_new(struct GuestFwd, 1);
|
|
fwd->hd = qemu_chr_new(buf, p, NULL);
|
|
if (!fwd->hd) {
|
|
error_report("could not open guest forwarding device '%s'", buf);
|
|
g_free(fwd);
|
|
return -1;
|
|
}
|
|
|
|
if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
|
|
error_report("conflicting/invalid host:port in guest forwarding "
|
|
"rule '%s'", config_str);
|
|
g_free(fwd);
|
|
return -1;
|
|
}
|
|
fwd->server = server;
|
|
fwd->port = port;
|
|
fwd->slirp = s->slirp;
|
|
|
|
qemu_chr_fe_claim_no_fail(fwd->hd);
|
|
qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
|
|
NULL, fwd);
|
|
}
|
|
return 0;
|
|
|
|
fail_syntax:
|
|
error_report("invalid guest forwarding rule '%s'", config_str);
|
|
return -1;
|
|
}
|
|
|
|
void hmp_info_usernet(Monitor *mon, const QDict *qdict)
|
|
{
|
|
SlirpState *s;
|
|
|
|
QTAILQ_FOREACH(s, &slirp_stacks, entry) {
|
|
int id;
|
|
bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0;
|
|
monitor_printf(mon, "VLAN %d (%s):\n",
|
|
got_vlan_id ? id : -1,
|
|
s->nc.name);
|
|
slirp_connection_info(s->slirp, mon);
|
|
}
|
|
}
|
|
|
|
static void
|
|
net_init_slirp_configs(const StringList *fwd, int flags)
|
|
{
|
|
while (fwd) {
|
|
struct slirp_config_str *config;
|
|
|
|
config = g_malloc0(sizeof(*config));
|
|
pstrcpy(config->str, sizeof(config->str), fwd->value->str);
|
|
config->flags = flags;
|
|
config->next = slirp_configs;
|
|
slirp_configs = config;
|
|
|
|
fwd = fwd->next;
|
|
}
|
|
}
|
|
|
|
static const char **slirp_dnssearch(const StringList *dnsname)
|
|
{
|
|
const StringList *c = dnsname;
|
|
size_t i = 0, num_opts = 0;
|
|
const char **ret;
|
|
|
|
while (c) {
|
|
num_opts++;
|
|
c = c->next;
|
|
}
|
|
|
|
if (num_opts == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
ret = g_malloc((num_opts + 1) * sizeof(*ret));
|
|
c = dnsname;
|
|
while (c) {
|
|
ret[i++] = c->value->str;
|
|
c = c->next;
|
|
}
|
|
ret[i] = NULL;
|
|
return ret;
|
|
}
|
|
|
|
int net_init_slirp(const Netdev *netdev, const char *name,
|
|
NetClientState *peer, Error **errp)
|
|
{
|
|
/* FIXME error_setg(errp, ...) on failure */
|
|
struct slirp_config_str *config;
|
|
char *vnet;
|
|
int ret;
|
|
const NetdevUserOptions *user;
|
|
const char **dnssearch;
|
|
bool ipv4 = true, ipv6 = true;
|
|
|
|
assert(netdev->type == NET_CLIENT_DRIVER_USER);
|
|
user = &netdev->u.user;
|
|
|
|
if ((user->has_ipv6 && user->ipv6 && !user->has_ipv4) ||
|
|
(user->has_ipv4 && !user->ipv4)) {
|
|
ipv4 = 0;
|
|
}
|
|
if ((user->has_ipv4 && user->ipv4 && !user->has_ipv6) ||
|
|
(user->has_ipv6 && !user->ipv6)) {
|
|
ipv6 = 0;
|
|
}
|
|
|
|
vnet = user->has_net ? g_strdup(user->net) :
|
|
user->has_ip ? g_strdup_printf("%s/24", user->ip) :
|
|
NULL;
|
|
|
|
dnssearch = slirp_dnssearch(user->dnssearch);
|
|
|
|
/* all optional fields are initialized to "all bits zero" */
|
|
|
|
net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
|
|
net_init_slirp_configs(user->guestfwd, 0);
|
|
|
|
ret = net_slirp_init(peer, "user", name, user->q_restrict,
|
|
ipv4, vnet, user->host,
|
|
ipv6, user->ipv6_prefix, user->ipv6_prefixlen,
|
|
user->ipv6_host, user->hostname, user->tftp,
|
|
user->bootfile, user->dhcpstart,
|
|
user->dns, user->ipv6_dns, user->smb,
|
|
user->smbserver, dnssearch);
|
|
|
|
while (slirp_configs) {
|
|
config = slirp_configs;
|
|
slirp_configs = config->next;
|
|
g_free(config);
|
|
}
|
|
|
|
g_free(vnet);
|
|
g_free(dnssearch);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
|
|
{
|
|
if (strcmp(opts_list->name, "net") != 0 ||
|
|
strncmp(optarg, "channel,", strlen("channel,")) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
error_report("The '-net channel' option is deprecated. "
|
|
"Please use '-netdev user,guestfwd=...' instead.");
|
|
|
|
/* handle legacy -net channel,port:chr */
|
|
optarg += strlen("channel,");
|
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
|
struct slirp_config_str *config;
|
|
|
|
config = g_malloc(sizeof(*config));
|
|
pstrcpy(config->str, sizeof(config->str), optarg);
|
|
config->flags = SLIRP_CFG_LEGACY;
|
|
config->next = slirp_configs;
|
|
slirp_configs = config;
|
|
*ret = 0;
|
|
} else {
|
|
*ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|