2009-11-25 21:48:54 +03: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.
|
|
|
|
*/
|
2018-02-01 14:18:39 +03:00
|
|
|
|
2016-01-29 20:50:00 +03:00
|
|
|
#include "qemu/osdep.h"
|
2018-11-14 15:36:33 +03:00
|
|
|
#include "qemu/log.h"
|
2009-11-25 21:48:54 +03:00
|
|
|
#include "net/slirp.h"
|
|
|
|
|
|
|
|
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2012-07-05 21:35:57 +04:00
|
|
|
#include <pwd.h>
|
2010-01-27 20:47:33 +03:00
|
|
|
#include <sys/wait.h>
|
|
|
|
#endif
|
net: Pad short frames to minimum size before sending from SLiRP/TAP
The minimum Ethernet frame length is 60 bytes. For short frames with
smaller length like ARP packets (only 42 bytes), on a real world NIC
it can choose either padding its length to the minimum required 60
bytes, or sending it out directly to the wire. Such behavior can be
hardcoded or controled by a register bit. Similarly on the receive
path, NICs can choose either dropping such short frames directly or
handing them over to software to handle.
On the other hand, for the network backends like SLiRP/TAP, they
don't expose a way to control the short frame behavior. As of today
they just send/receive data from/to the other end connected to them,
which means any sized packet is acceptable. So they can send and
receive short frames without any problem. It is observed that ARP
packets sent from SLiRP/TAP are 42 bytes, and SLiRP/TAP just send
these ARP packets to the other end which might be a NIC model that
does not allow short frames to pass through.
To provide better compatibility, for packets sent from QEMU network
backends like SLiRP/TAP, we change to pad short frames before sending
it out to the other end, if the other end does not forbid it via the
nc->do_not_pad flag. This ensures a backend as an Ethernet sender
does not violate the spec. But with this change, the behavior of
dropping short frames from SLiRP/TAP interfaces in the NIC model
cannot be emulated because it always receives a packet that is spec
complaint. The capability of sending short frames from NIC models is
still supported and short frames can still pass through SLiRP/TAP.
This commit should be able to fix the issue as reported with some
NIC models before, that ARP requests get dropped, preventing the
guest from becoming visible on the network. It was workarounded in
these NIC models on the receive path, that when a short frame is
received, it is padded up to 60 bytes.
The following 2 commits seem to be the one to workaround this issue
in e1000 and vmxenet3 before, and should probably be reverted.
commit 78aeb23eded2 ("e1000: Pad short frames to minimum size (60 bytes)")
commit 40a87c6c9b11 ("vmxnet3: Pad short frames to minimum size (60 bytes)")
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
2021-03-17 09:26:29 +03:00
|
|
|
#include "net/eth.h"
|
2012-10-24 10:43:34 +04:00
|
|
|
#include "net/net.h"
|
2012-09-17 20:43:51 +04:00
|
|
|
#include "clients.h"
|
|
|
|
#include "hub.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "monitor/monitor.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/sockets.h"
|
2019-02-12 19:25:23 +03:00
|
|
|
#include <libslirp.h>
|
2017-01-26 17:26:44 +03:00
|
|
|
#include "chardev/char-fe.h"
|
2016-07-12 10:57:12 +03:00
|
|
|
#include "sysemu/sysemu.h"
|
2016-03-20 20:16:19 +03:00
|
|
|
#include "qemu/cutils.h"
|
2016-10-22 12:52:52 +03:00
|
|
|
#include "qapi/error.h"
|
2018-02-01 14:18:39 +03:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2018-11-14 15:36:40 +03:00
|
|
|
#include "util.h"
|
2019-02-12 19:25:19 +03:00
|
|
|
#include "migration/register.h"
|
2023-10-20 12:07:23 +03:00
|
|
|
#include "migration/vmstate.h"
|
2019-02-12 19:25:19 +03:00
|
|
|
#include "migration/qemu-file-types.h"
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
struct slirp_config_str {
|
|
|
|
struct slirp_config_str *next;
|
|
|
|
int flags;
|
|
|
|
char str[1024];
|
|
|
|
};
|
|
|
|
|
2019-01-17 14:43:35 +03:00
|
|
|
struct GuestFwd {
|
|
|
|
CharBackend hd;
|
|
|
|
struct in_addr server;
|
|
|
|
int port;
|
|
|
|
Slirp *slirp;
|
|
|
|
};
|
|
|
|
|
2009-11-25 21:48:54 +03:00
|
|
|
typedef struct SlirpState {
|
2012-07-24 19:35:13 +04:00
|
|
|
NetClientState nc;
|
2009-11-25 21:48:54 +03:00
|
|
|
QTAILQ_ENTRY(SlirpState) entry;
|
|
|
|
Slirp *slirp;
|
2019-01-17 14:43:55 +03:00
|
|
|
Notifier poll_notifier;
|
2016-07-12 10:57:12 +03:00
|
|
|
Notifier exit_notifier;
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2017-04-07 17:32:54 +03:00
|
|
|
gchar *smb_dir;
|
2009-11-25 21:48:54 +03:00
|
|
|
#endif
|
2019-01-17 14:43:35 +03:00
|
|
|
GSList *fwd;
|
2009-11-25 21:48:54 +03:00
|
|
|
} SlirpState;
|
|
|
|
|
|
|
|
static struct slirp_config_str *slirp_configs;
|
2018-12-06 13:58:10 +03:00
|
|
|
static QTAILQ_HEAD(, SlirpState) slirp_stacks =
|
2009-11-25 21:48:54 +03:00
|
|
|
QTAILQ_HEAD_INITIALIZER(slirp_stacks);
|
|
|
|
|
2018-08-22 16:43:30 +03:00
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp);
|
|
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2009-11-25 21:48:54 +03:00
|
|
|
static int slirp_smb(SlirpState *s, const char *exported_dir,
|
2017-07-15 19:43:50 +03:00
|
|
|
struct in_addr vserver_addr, Error **errp);
|
2009-11-25 21:48:54 +03:00
|
|
|
static void slirp_smb_cleanup(SlirpState *s);
|
|
|
|
#else
|
|
|
|
static inline void slirp_smb_cleanup(SlirpState *s) { }
|
|
|
|
#endif
|
|
|
|
|
2019-01-17 14:43:54 +03:00
|
|
|
static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len,
|
|
|
|
void *opaque)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
|
|
|
SlirpState *s = opaque;
|
net: Pad short frames to minimum size before sending from SLiRP/TAP
The minimum Ethernet frame length is 60 bytes. For short frames with
smaller length like ARP packets (only 42 bytes), on a real world NIC
it can choose either padding its length to the minimum required 60
bytes, or sending it out directly to the wire. Such behavior can be
hardcoded or controled by a register bit. Similarly on the receive
path, NICs can choose either dropping such short frames directly or
handing them over to software to handle.
On the other hand, for the network backends like SLiRP/TAP, they
don't expose a way to control the short frame behavior. As of today
they just send/receive data from/to the other end connected to them,
which means any sized packet is acceptable. So they can send and
receive short frames without any problem. It is observed that ARP
packets sent from SLiRP/TAP are 42 bytes, and SLiRP/TAP just send
these ARP packets to the other end which might be a NIC model that
does not allow short frames to pass through.
To provide better compatibility, for packets sent from QEMU network
backends like SLiRP/TAP, we change to pad short frames before sending
it out to the other end, if the other end does not forbid it via the
nc->do_not_pad flag. This ensures a backend as an Ethernet sender
does not violate the spec. But with this change, the behavior of
dropping short frames from SLiRP/TAP interfaces in the NIC model
cannot be emulated because it always receives a packet that is spec
complaint. The capability of sending short frames from NIC models is
still supported and short frames can still pass through SLiRP/TAP.
This commit should be able to fix the issue as reported with some
NIC models before, that ARP requests get dropped, preventing the
guest from becoming visible on the network. It was workarounded in
these NIC models on the receive path, that when a short frame is
received, it is padded up to 60 bytes.
The following 2 commits seem to be the one to workaround this issue
in e1000 and vmxenet3 before, and should probably be reverted.
commit 78aeb23eded2 ("e1000: Pad short frames to minimum size (60 bytes)")
commit 40a87c6c9b11 ("vmxnet3: Pad short frames to minimum size (60 bytes)")
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
2021-03-17 09:26:29 +03:00
|
|
|
uint8_t min_pkt[ETH_ZLEN];
|
|
|
|
size_t min_pktsz = sizeof(min_pkt);
|
|
|
|
|
2021-04-23 06:18:03 +03:00
|
|
|
if (net_peer_needs_padding(&s->nc)) {
|
net: Pad short frames to minimum size before sending from SLiRP/TAP
The minimum Ethernet frame length is 60 bytes. For short frames with
smaller length like ARP packets (only 42 bytes), on a real world NIC
it can choose either padding its length to the minimum required 60
bytes, or sending it out directly to the wire. Such behavior can be
hardcoded or controled by a register bit. Similarly on the receive
path, NICs can choose either dropping such short frames directly or
handing them over to software to handle.
On the other hand, for the network backends like SLiRP/TAP, they
don't expose a way to control the short frame behavior. As of today
they just send/receive data from/to the other end connected to them,
which means any sized packet is acceptable. So they can send and
receive short frames without any problem. It is observed that ARP
packets sent from SLiRP/TAP are 42 bytes, and SLiRP/TAP just send
these ARP packets to the other end which might be a NIC model that
does not allow short frames to pass through.
To provide better compatibility, for packets sent from QEMU network
backends like SLiRP/TAP, we change to pad short frames before sending
it out to the other end, if the other end does not forbid it via the
nc->do_not_pad flag. This ensures a backend as an Ethernet sender
does not violate the spec. But with this change, the behavior of
dropping short frames from SLiRP/TAP interfaces in the NIC model
cannot be emulated because it always receives a packet that is spec
complaint. The capability of sending short frames from NIC models is
still supported and short frames can still pass through SLiRP/TAP.
This commit should be able to fix the issue as reported with some
NIC models before, that ARP requests get dropped, preventing the
guest from becoming visible on the network. It was workarounded in
these NIC models on the receive path, that when a short frame is
received, it is padded up to 60 bytes.
The following 2 commits seem to be the one to workaround this issue
in e1000 and vmxenet3 before, and should probably be reverted.
commit 78aeb23eded2 ("e1000: Pad short frames to minimum size (60 bytes)")
commit 40a87c6c9b11 ("vmxnet3: Pad short frames to minimum size (60 bytes)")
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
2021-03-17 09:26:29 +03:00
|
|
|
if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pkt_len)) {
|
|
|
|
pkt = min_pkt;
|
|
|
|
pkt_len = min_pktsz;
|
|
|
|
}
|
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2019-01-17 14:43:54 +03:00
|
|
|
return qemu_send_packet(&s->nc, pkt, pkt_len);
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2009-11-25 21:49:06 +03:00
|
|
|
SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
slirp_input(s->slirp, buf, size);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2016-07-12 10:57:12 +03:00
|
|
|
static void slirp_smb_exit(Notifier *n, void *data)
|
|
|
|
{
|
|
|
|
SlirpState *s = container_of(n, SlirpState, exit_notifier);
|
|
|
|
slirp_smb_cleanup(s);
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:35 +03:00
|
|
|
static void slirp_free_fwd(gpointer data)
|
|
|
|
{
|
|
|
|
struct GuestFwd *fwd = data;
|
|
|
|
|
|
|
|
qemu_chr_fe_deinit(&fwd->hd, true);
|
|
|
|
g_free(data);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
static void net_slirp_cleanup(NetClientState *nc)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2009-11-25 21:49:06 +03:00
|
|
|
SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2019-01-17 14:43:35 +03:00
|
|
|
g_slist_free_full(s->fwd, slirp_free_fwd);
|
2019-01-17 14:43:55 +03:00
|
|
|
main_loop_poll_remove_notifier(&s->poll_notifier);
|
2019-02-12 19:25:19 +03:00
|
|
|
unregister_savevm(NULL, "slirp", s->slirp);
|
2009-11-25 21:48:54 +03:00
|
|
|
slirp_cleanup(s->slirp);
|
2016-08-18 16:44:05 +03:00
|
|
|
if (s->exit_notifier.notify) {
|
|
|
|
qemu_remove_exit_notifier(&s->exit_notifier);
|
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
slirp_smb_cleanup(s);
|
|
|
|
QTAILQ_REMOVE(&slirp_stacks, s, entry);
|
|
|
|
}
|
|
|
|
|
2009-11-25 21:49:06 +03:00
|
|
|
static NetClientInfo net_slirp_info = {
|
qapi: Change Netdev into a flat union
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>
2016-07-14 06:50:23 +03:00
|
|
|
.type = NET_CLIENT_DRIVER_USER,
|
2009-11-25 21:49:06 +03:00
|
|
|
.size = sizeof(SlirpState),
|
|
|
|
.receive = net_slirp_receive,
|
|
|
|
.cleanup = net_slirp_cleanup,
|
|
|
|
};
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_guest_error(const char *msg, void *opaque)
|
2018-11-14 15:36:33 +03:00
|
|
|
{
|
|
|
|
qemu_log_mask(LOG_GUEST_ERROR, "%s", msg);
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static int64_t net_slirp_clock_get_ns(void *opaque)
|
2018-11-22 01:06:28 +03:00
|
|
|
{
|
|
|
|
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
|
|
}
|
|
|
|
|
2022-04-11 10:39:16 +03:00
|
|
|
typedef struct SlirpTimer SlirpTimer;
|
2022-04-11 10:26:06 +03:00
|
|
|
struct SlirpTimer {
|
|
|
|
QEMUTimer timer;
|
2022-04-11 10:39:16 +03:00
|
|
|
#if SLIRP_CHECK_VERSION(4,7,0)
|
|
|
|
Slirp *slirp;
|
|
|
|
SlirpTimerId id;
|
|
|
|
void *cb_opaque;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
#if SLIRP_CHECK_VERSION(4,7,0)
|
|
|
|
static void net_slirp_init_completed(Slirp *slirp, void *opaque)
|
|
|
|
{
|
|
|
|
SlirpState *s = opaque;
|
|
|
|
s->slirp = slirp;
|
2022-04-11 10:26:06 +03:00
|
|
|
}
|
|
|
|
|
2022-04-11 10:39:16 +03:00
|
|
|
static void net_slirp_timer_cb(void *opaque)
|
|
|
|
{
|
|
|
|
SlirpTimer *t = opaque;
|
|
|
|
slirp_handle_timer(t->slirp, t->id, t->cb_opaque);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *net_slirp_timer_new_opaque(SlirpTimerId id,
|
|
|
|
void *cb_opaque, void *opaque)
|
|
|
|
{
|
|
|
|
SlirpState *s = opaque;
|
|
|
|
SlirpTimer *t = g_new(SlirpTimer, 1);
|
|
|
|
t->slirp = s->slirp;
|
|
|
|
t->id = id;
|
|
|
|
t->cb_opaque = cb_opaque;
|
|
|
|
timer_init_full(&t->timer, NULL, QEMU_CLOCK_VIRTUAL,
|
|
|
|
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
|
|
|
net_slirp_timer_cb, t);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
#else
|
2019-01-17 14:43:58 +03:00
|
|
|
static void *net_slirp_timer_new(SlirpTimerCb cb,
|
|
|
|
void *cb_opaque, void *opaque)
|
2019-01-17 14:43:37 +03:00
|
|
|
{
|
2022-04-11 10:26:06 +03:00
|
|
|
SlirpTimer *t = g_new(SlirpTimer, 1);
|
|
|
|
timer_init_full(&t->timer, NULL, QEMU_CLOCK_VIRTUAL,
|
|
|
|
SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL,
|
|
|
|
cb, cb_opaque);
|
|
|
|
return t;
|
2019-01-17 14:43:37 +03:00
|
|
|
}
|
2022-04-11 10:39:16 +03:00
|
|
|
#endif
|
2019-01-17 14:43:37 +03:00
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_timer_free(void *timer, void *opaque)
|
2019-01-17 14:43:37 +03:00
|
|
|
{
|
2022-04-11 10:26:06 +03:00
|
|
|
SlirpTimer *t = timer;
|
|
|
|
timer_del(&t->timer);
|
|
|
|
g_free(t);
|
2019-01-17 14:43:37 +03:00
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_timer_mod(void *timer, int64_t expire_timer,
|
|
|
|
void *opaque)
|
2019-01-17 14:43:37 +03:00
|
|
|
{
|
2022-04-11 10:26:06 +03:00
|
|
|
SlirpTimer *t = timer;
|
|
|
|
timer_mod(&t->timer, expire_timer);
|
2019-01-17 14:43:37 +03:00
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_register_poll_fd(int fd, void *opaque)
|
2019-01-17 14:43:41 +03:00
|
|
|
{
|
2023-02-21 15:47:56 +03:00
|
|
|
#ifdef WIN32
|
|
|
|
AioContext *ctxt = qemu_get_aio_context();
|
|
|
|
|
2023-02-21 15:47:58 +03:00
|
|
|
if (WSAEventSelect(fd, event_notifier_get_handle(&ctxt->notifier),
|
2023-02-21 15:47:56 +03:00
|
|
|
FD_READ | FD_ACCEPT | FD_CLOSE |
|
2023-02-21 15:47:58 +03:00
|
|
|
FD_CONNECT | FD_WRITE | FD_OOB) != 0) {
|
|
|
|
error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()");
|
|
|
|
}
|
2023-02-21 15:47:56 +03:00
|
|
|
#endif
|
2019-01-17 14:43:41 +03:00
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_unregister_poll_fd(int fd, void *opaque)
|
2019-01-17 14:43:42 +03:00
|
|
|
{
|
2023-02-21 15:47:57 +03:00
|
|
|
#ifdef WIN32
|
2023-02-21 15:47:58 +03:00
|
|
|
if (WSAEventSelect(fd, NULL, 0) != 0) {
|
|
|
|
error_setg_win32(&error_warn, WSAGetLastError(), "failed to WSAEventSelect()");
|
|
|
|
}
|
2023-02-21 15:47:57 +03:00
|
|
|
#endif
|
2019-01-17 14:43:42 +03:00
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:58 +03:00
|
|
|
static void net_slirp_notify(void *opaque)
|
|
|
|
{
|
|
|
|
qemu_notify_event();
|
|
|
|
}
|
|
|
|
|
2018-11-14 15:36:07 +03:00
|
|
|
static const SlirpCb slirp_cb = {
|
2019-01-17 14:43:54 +03:00
|
|
|
.send_packet = net_slirp_send_packet,
|
2018-11-14 15:36:33 +03:00
|
|
|
.guest_error = net_slirp_guest_error,
|
2018-11-22 01:06:28 +03:00
|
|
|
.clock_get_ns = net_slirp_clock_get_ns,
|
2022-04-11 10:39:16 +03:00
|
|
|
#if SLIRP_CHECK_VERSION(4,7,0)
|
|
|
|
.init_completed = net_slirp_init_completed,
|
|
|
|
.timer_new_opaque = net_slirp_timer_new_opaque,
|
|
|
|
#else
|
2019-01-17 14:43:37 +03:00
|
|
|
.timer_new = net_slirp_timer_new,
|
2022-04-11 10:39:16 +03:00
|
|
|
#endif
|
2019-01-17 14:43:37 +03:00
|
|
|
.timer_free = net_slirp_timer_free,
|
|
|
|
.timer_mod = net_slirp_timer_mod,
|
2019-01-17 14:43:41 +03:00
|
|
|
.register_poll_fd = net_slirp_register_poll_fd,
|
2019-01-17 14:43:42 +03:00
|
|
|
.unregister_poll_fd = net_slirp_unregister_poll_fd,
|
2019-01-17 14:43:58 +03:00
|
|
|
.notify = net_slirp_notify,
|
2018-11-14 15:36:07 +03:00
|
|
|
};
|
|
|
|
|
2019-01-17 14:43:57 +03:00
|
|
|
static int slirp_poll_to_gio(int events)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (events & SLIRP_POLL_IN) {
|
|
|
|
ret |= G_IO_IN;
|
|
|
|
}
|
|
|
|
if (events & SLIRP_POLL_OUT) {
|
|
|
|
ret |= G_IO_OUT;
|
|
|
|
}
|
|
|
|
if (events & SLIRP_POLL_PRI) {
|
|
|
|
ret |= G_IO_PRI;
|
|
|
|
}
|
|
|
|
if (events & SLIRP_POLL_ERR) {
|
|
|
|
ret |= G_IO_ERR;
|
|
|
|
}
|
|
|
|
if (events & SLIRP_POLL_HUP) {
|
|
|
|
ret |= G_IO_HUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_slirp_add_poll(int fd, int events, void *opaque)
|
|
|
|
{
|
|
|
|
GArray *pollfds = opaque;
|
|
|
|
GPollFD pfd = {
|
|
|
|
.fd = fd,
|
|
|
|
.events = slirp_poll_to_gio(events),
|
|
|
|
};
|
|
|
|
int idx = pollfds->len;
|
|
|
|
g_array_append_val(pollfds, pfd);
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int slirp_gio_to_poll(int events)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (events & G_IO_IN) {
|
|
|
|
ret |= SLIRP_POLL_IN;
|
|
|
|
}
|
|
|
|
if (events & G_IO_OUT) {
|
|
|
|
ret |= SLIRP_POLL_OUT;
|
|
|
|
}
|
|
|
|
if (events & G_IO_PRI) {
|
|
|
|
ret |= SLIRP_POLL_PRI;
|
|
|
|
}
|
|
|
|
if (events & G_IO_ERR) {
|
|
|
|
ret |= SLIRP_POLL_ERR;
|
|
|
|
}
|
|
|
|
if (events & G_IO_HUP) {
|
|
|
|
ret |= SLIRP_POLL_HUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_slirp_get_revents(int idx, void *opaque)
|
|
|
|
{
|
|
|
|
GArray *pollfds = opaque;
|
|
|
|
|
|
|
|
return slirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents);
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:55 +03:00
|
|
|
static void net_slirp_poll_notify(Notifier *notifier, void *data)
|
|
|
|
{
|
|
|
|
MainLoopPoll *poll = data;
|
|
|
|
SlirpState *s = container_of(notifier, SlirpState, poll_notifier);
|
|
|
|
|
|
|
|
switch (poll->state) {
|
|
|
|
case MAIN_LOOP_POLL_FILL:
|
2019-01-17 14:43:57 +03:00
|
|
|
slirp_pollfds_fill(s->slirp, &poll->timeout,
|
|
|
|
net_slirp_add_poll, poll->pollfds);
|
2019-01-17 14:43:55 +03:00
|
|
|
break;
|
|
|
|
case MAIN_LOOP_POLL_OK:
|
|
|
|
case MAIN_LOOP_POLL_ERR:
|
2019-01-17 14:43:57 +03:00
|
|
|
slirp_pollfds_poll(s->slirp, poll->state == MAIN_LOOP_POLL_ERR,
|
|
|
|
net_slirp_get_revents, poll->pollfds);
|
2019-01-17 14:43:55 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-12 19:25:19 +03:00
|
|
|
static ssize_t
|
|
|
|
net_slirp_stream_read(void *buf, size_t size, void *opaque)
|
|
|
|
{
|
|
|
|
QEMUFile *f = opaque;
|
|
|
|
|
|
|
|
return qemu_get_buffer(f, buf, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
net_slirp_stream_write(const void *buf, size_t size, void *opaque)
|
|
|
|
{
|
|
|
|
QEMUFile *f = opaque;
|
|
|
|
|
|
|
|
qemu_put_buffer(f, buf, size);
|
|
|
|
if (qemu_file_get_error(f)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int net_slirp_state_load(QEMUFile *f, void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
Slirp *slirp = opaque;
|
|
|
|
|
|
|
|
return slirp_state_load(slirp, version_id, net_slirp_stream_read, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void net_slirp_state_save(QEMUFile *f, void *opaque)
|
|
|
|
{
|
|
|
|
Slirp *slirp = opaque;
|
|
|
|
|
|
|
|
slirp_state_save(slirp, net_slirp_stream_write, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static SaveVMHandlers savevm_slirp_state = {
|
|
|
|
.save_state = net_slirp_state_save,
|
|
|
|
.load_state = net_slirp_state_load,
|
|
|
|
};
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
static int net_slirp_init(NetClientState *peer, const char *model,
|
2009-11-25 21:48:54 +03:00
|
|
|
const char *name, int restricted,
|
2016-03-20 14:29:54 +03:00
|
|
|
bool ipv4, const char *vnetwork, const char *vhost,
|
|
|
|
bool ipv6, const char *vprefix6, int vprefix6_len,
|
2016-03-15 12:31:22 +03:00
|
|
|
const char *vhost6,
|
2009-11-25 21:48:54 +03:00
|
|
|
const char *vhostname, const char *tftp_export,
|
|
|
|
const char *bootfile, const char *vdhcp_start,
|
2016-03-15 12:31:22 +03:00
|
|
|
const char *vnameserver, const char *vnameserver6,
|
|
|
|
const char *smb_export, const char *vsmbserver,
|
2018-02-27 19:06:01 +03:00
|
|
|
const char **dnssearch, const char *vdomainname,
|
2018-09-14 10:26:16 +03:00
|
|
|
const char *tftp_server_name,
|
2018-02-27 19:06:01 +03:00
|
|
|
Error **errp)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
|
|
|
/* 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 */
|
2016-03-15 12:31:22 +03:00
|
|
|
struct in6_addr ip6_prefix;
|
|
|
|
struct in6_addr ip6_host;
|
|
|
|
struct in6_addr ip6_dns;
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2009-11-25 21:48:54 +03:00
|
|
|
struct in_addr smbsrv = { .s_addr = 0 };
|
|
|
|
#endif
|
2022-04-11 11:16:36 +03:00
|
|
|
SlirpConfig cfg = { 0 };
|
2012-07-24 19:35:13 +04:00
|
|
|
NetClientState *nc;
|
2009-11-25 21:48:54 +03:00
|
|
|
SlirpState *s;
|
|
|
|
char buf[20];
|
|
|
|
uint32_t addr;
|
|
|
|
int shift;
|
|
|
|
char *end;
|
|
|
|
struct slirp_config_str *config;
|
|
|
|
|
2016-03-20 14:29:54 +03:00
|
|
|
if (!ipv4 && (vnetwork || vhost || vnameserver)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "IPv4 disabled but netmask/host/dns provided");
|
2016-03-20 14:29:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ipv6 && (vprefix6 || vhost6 || vnameserver6)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "IPv6 disabled but prefix/host6/dns6 provided");
|
2016-03-20 14:29:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ipv4 && !ipv6) {
|
|
|
|
/* It doesn't make sense to disable both */
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "IPv4 and IPv6 disabled");
|
2016-03-20 14:29:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-11-25 21:48:54 +03:00
|
|
|
if (vnetwork) {
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
|
|
|
|
if (!inet_aton(vnetwork, &net)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse netmask");
|
2009-11-25 21:48:54 +03:00
|
|
|
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)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse netmask");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
shift = strtol(vnetwork, &end, 10);
|
|
|
|
if (*end != '\0') {
|
|
|
|
if (!inet_aton(vnetwork, &mask)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp,
|
|
|
|
"Failed to parse netmask (trailing chars)");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else if (shift < 4 || shift > 32) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp,
|
|
|
|
"Invalid netmask provided (must be in range 4-32)");
|
2009-11-25 21:48:54 +03:00
|
|
|
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)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse host");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((host.s_addr & mask.s_addr) != net.s_addr) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Host doesn't belong to network");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-03 17:11:49 +04:00
|
|
|
if (vnameserver && !inet_aton(vnameserver, &dns)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse DNS");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2019-09-29 21:08:20 +03:00
|
|
|
if (restricted && (dns.s_addr & mask.s_addr) != net.s_addr) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "DNS doesn't belong to network");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (dns.s_addr == host.s_addr) {
|
|
|
|
error_setg(errp, "DNS must be different from host");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-06-03 17:11:49 +04:00
|
|
|
if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse DHCP start address");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2017-07-15 19:43:50 +03:00
|
|
|
if ((dhcp.s_addr & mask.s_addr) != net.s_addr) {
|
|
|
|
error_setg(errp, "DHCP doesn't belong to network");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
|
2021-01-22 03:42:51 +03:00
|
|
|
error_setg(errp, "DHCP must be different from host and DNS");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2009-11-25 21:48:54 +03:00
|
|
|
if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse SMB address");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-15 12:31:22 +03:00
|
|
|
if (!vprefix6) {
|
|
|
|
vprefix6 = "fec0::";
|
|
|
|
}
|
|
|
|
if (!inet_pton(AF_INET6, vprefix6, &ip6_prefix)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse IPv6 prefix");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vprefix6_len) {
|
|
|
|
vprefix6_len = 64;
|
|
|
|
}
|
|
|
|
if (vprefix6_len < 0 || vprefix6_len > 126) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp,
|
2019-05-15 12:08:05 +03:00
|
|
|
"Invalid IPv6 prefix provided "
|
|
|
|
"(IPv6 prefix length must be between 0 and 126)");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vhost6) {
|
|
|
|
if (!inet_pton(AF_INET6, vhost6, &ip6_host)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse IPv6 host");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!in6_equal_net(&ip6_prefix, &ip6_host, vprefix6_len)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "IPv6 Host doesn't belong to network");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ip6_host = ip6_prefix;
|
|
|
|
ip6_host.s6_addr[15] |= 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vnameserver6) {
|
|
|
|
if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to parse IPv6 DNS");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2019-09-29 21:08:20 +03:00
|
|
|
if (restricted && !in6_equal_net(&ip6_prefix, &ip6_dns, vprefix6_len)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "IPv6 DNS doesn't belong to network");
|
2016-03-15 12:31:22 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ip6_dns = ip6_prefix;
|
|
|
|
ip6_dns.s6_addr[15] |= 3;
|
|
|
|
}
|
|
|
|
|
2018-02-27 19:06:01 +03:00
|
|
|
if (vdomainname && !*vdomainname) {
|
|
|
|
error_setg(errp, "'domainname' parameter cannot be empty");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-09-14 10:26:15 +03:00
|
|
|
if (vdomainname && strlen(vdomainname) > 255) {
|
|
|
|
error_setg(errp, "'domainname' parameter cannot exceed 255 bytes");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vhostname && strlen(vhostname) > 255) {
|
|
|
|
error_setg(errp, "'vhostname' parameter cannot exceed 255 bytes");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-03-15 12:31:22 +03:00
|
|
|
|
2018-09-14 10:26:16 +03:00
|
|
|
if (tftp_server_name && strlen(tftp_server_name) > 255) {
|
|
|
|
error_setg(errp, "'tftp-server-name' parameter cannot exceed 255 bytes");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:08 +04:00
|
|
|
nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
|
2009-11-25 21:49:06 +03:00
|
|
|
|
2022-10-21 12:09:10 +03:00
|
|
|
qemu_set_info_str(nc, "net=%s,restrict=%s", inet_ntoa(net),
|
|
|
|
restricted ? "on" : "off");
|
2021-04-02 06:03:12 +03:00
|
|
|
|
2009-11-25 21:49:06 +03:00
|
|
|
s = DO_UPCAST(SlirpState, nc, nc);
|
|
|
|
|
2022-04-11 10:39:16 +03:00
|
|
|
cfg.version = SLIRP_CHECK_VERSION(4,7,0) ? 4 : 1;
|
2022-04-11 11:16:36 +03:00
|
|
|
cfg.restricted = restricted;
|
|
|
|
cfg.in_enabled = ipv4;
|
|
|
|
cfg.vnetwork = net;
|
|
|
|
cfg.vnetmask = mask;
|
|
|
|
cfg.vhost = host;
|
|
|
|
cfg.in6_enabled = ipv6;
|
|
|
|
cfg.vprefix_addr6 = ip6_prefix;
|
|
|
|
cfg.vprefix_len = vprefix6_len;
|
|
|
|
cfg.vhost6 = ip6_host;
|
|
|
|
cfg.vhostname = vhostname;
|
|
|
|
cfg.tftp_server_name = tftp_server_name;
|
|
|
|
cfg.tftp_path = tftp_export;
|
|
|
|
cfg.bootfile = bootfile;
|
|
|
|
cfg.vdhcp_start = dhcp;
|
|
|
|
cfg.vnameserver = dns;
|
|
|
|
cfg.vnameserver6 = ip6_dns;
|
|
|
|
cfg.vdnssearch = dnssearch;
|
|
|
|
cfg.vdomainname = vdomainname;
|
|
|
|
s->slirp = slirp_new(&cfg, &slirp_cb, s);
|
2009-11-25 21:48:54 +03:00
|
|
|
QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
|
|
|
|
|
2019-02-12 19:25:19 +03:00
|
|
|
/*
|
|
|
|
* Make sure the current bitstream version of slirp is 4, to avoid
|
|
|
|
* QEMU migration incompatibilities, if upstream slirp bumped the
|
|
|
|
* version.
|
|
|
|
*
|
|
|
|
* FIXME: use bitfields of features? teach libslirp to save with
|
|
|
|
* specific version?
|
|
|
|
*/
|
|
|
|
g_assert(slirp_state_version() == 4);
|
2023-10-20 12:07:23 +03:00
|
|
|
register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY,
|
|
|
|
slirp_state_version(), &savevm_slirp_state, s->slirp);
|
2019-02-12 19:25:19 +03:00
|
|
|
|
2019-01-17 14:43:55 +03:00
|
|
|
s->poll_notifier.notify = net_slirp_poll_notify;
|
|
|
|
main_loop_poll_add_notifier(&s->poll_notifier);
|
|
|
|
|
2009-11-25 21:48:54 +03:00
|
|
|
for (config = slirp_configs; config; config = config->next) {
|
|
|
|
if (config->flags & SLIRP_CFG_HOSTFWD) {
|
2018-08-22 16:43:30 +03:00
|
|
|
if (slirp_hostfwd(s, config->str, errp) < 0) {
|
2009-11-25 21:49:06 +03:00
|
|
|
goto error;
|
2017-07-15 19:43:50 +03:00
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
} else {
|
2018-08-22 16:43:30 +03:00
|
|
|
if (slirp_guestfwd(s, config->str, errp) < 0) {
|
2009-11-25 21:49:06 +03:00
|
|
|
goto error;
|
2017-07-15 19:43:50 +03:00
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
}
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2009-11-25 21:48:54 +03:00
|
|
|
if (smb_export) {
|
2017-07-15 19:43:50 +03:00
|
|
|
if (slirp_smb(s, smb_export, smbsrv, errp) < 0) {
|
2009-11-25 21:49:06 +03:00
|
|
|
goto error;
|
2017-07-15 19:43:50 +03:00
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-07-12 10:57:12 +03:00
|
|
|
s->exit_notifier.notify = slirp_smb_exit;
|
|
|
|
qemu_add_exit_notifier(&s->exit_notifier);
|
2009-11-25 21:48:54 +03:00
|
|
|
return 0;
|
2009-11-25 21:49:06 +03:00
|
|
|
|
|
|
|
error:
|
2012-07-24 19:35:15 +04:00
|
|
|
qemu_del_net_client(nc);
|
2009-11-25 21:49:06 +03:00
|
|
|
return -1;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
|
2019-12-05 13:41:09 +03:00
|
|
|
static SlirpState *slirp_lookup(Monitor *mon, const char *id)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2019-12-05 13:41:09 +03:00
|
|
|
if (id) {
|
|
|
|
NetClientState *nc = qemu_find_netdev(id);
|
|
|
|
if (!nc) {
|
|
|
|
monitor_printf(mon, "unrecognized netdev id '%s'\n", id);
|
|
|
|
return NULL;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
2009-11-25 21:49:06 +03:00
|
|
|
if (strcmp(nc->model, "user")) {
|
2009-11-25 21:48:54 +03:00
|
|
|
monitor_printf(mon, "invalid device specified\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-11-25 21:49:06 +03:00
|
|
|
return DO_UPCAST(SlirpState, nc, nc);
|
2009-11-25 21:48:54 +03:00
|
|
|
} else {
|
|
|
|
if (QTAILQ_EMPTY(&slirp_stacks)) {
|
|
|
|
monitor_printf(mon, "user mode network stack not in use\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return QTAILQ_FIRST(&slirp_stacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
hmp: Name HMP command handler functions hmp_COMMAND()
Some are called do_COMMAND() (old ones, usually), some hmp_COMMAND(),
and sometimes COMMAND pointlessly differs in spelling.
Normalize to hmp_COMMAND(), where COMMAND is exactly the command name
with '-' replaced by '_'.
Exceptions:
* do_device_add() and client_migrate_info() *not* renamed to
hmp_device_add(), hmp_client_migrate_info(), because they're also
QMP handlers. They still need to be converted to QAPI.
* do_memory_dump(), do_physical_memory_dump(), do_ioport_read(),
do_ioport_write() renamed do hmp_* instead of hmp_x(), hmp_xp(),
hmp_i(), hmp_o(), because those names are too cryptic for my taste.
* do_info_help() renamed to hmp_info_help() instead of hmp_info(),
because it only covers help.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-02-06 15:55:43 +03:00
|
|
|
void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2021-09-26 00:48:20 +03:00
|
|
|
struct sockaddr_in host_addr = {
|
|
|
|
.sin_family = AF_INET,
|
|
|
|
.sin_addr = {
|
|
|
|
.s_addr = INADDR_ANY,
|
|
|
|
},
|
|
|
|
};
|
2009-11-25 21:48:54 +03:00
|
|
|
int host_port;
|
2011-11-16 18:45:59 +04:00
|
|
|
char buf[256];
|
2009-11-25 21:48:54 +03:00
|
|
|
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");
|
|
|
|
|
2019-12-05 13:41:09 +03:00
|
|
|
if (arg2) {
|
|
|
|
s = slirp_lookup(mon, arg1);
|
2018-01-11 23:02:40 +03:00
|
|
|
src_str = arg2;
|
2009-11-25 21:48:54 +03:00
|
|
|
} else {
|
2019-12-05 13:41:09 +03:00
|
|
|
s = slirp_lookup(mon, NULL);
|
2009-11-25 21:48:54 +03:00
|
|
|
src_str = arg1;
|
|
|
|
}
|
|
|
|
if (!s) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = src_str;
|
2011-11-16 18:45:59 +04:00
|
|
|
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) {
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2018-03-16 17:39:21 +03:00
|
|
|
if (qemu_strtoi(p, NULL, 10, &host_port)) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
host_addr.sin_port = htons(host_port);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2021-09-26 00:48:20 +03:00
|
|
|
#if SLIRP_CHECK_VERSION(4, 5, 0)
|
|
|
|
err = slirp_remove_hostxfwd(s->slirp, (struct sockaddr *) &host_addr,
|
|
|
|
sizeof(host_addr), is_udp ? SLIRP_HOSTFWD_UDP : 0);
|
|
|
|
#else
|
|
|
|
err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr.sin_addr, host_port);
|
|
|
|
#endif
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
|
2011-12-17 13:23:59 +04:00
|
|
|
err ? "not found" : "removed");
|
2009-11-25 21:48:54 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail_syntax:
|
|
|
|
monitor_printf(mon, "invalid format\n");
|
|
|
|
}
|
|
|
|
|
2018-08-22 16:43:30 +03:00
|
|
|
static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2021-09-26 00:48:20 +03:00
|
|
|
struct sockaddr_in host_addr = {
|
|
|
|
.sin_family = AF_INET,
|
|
|
|
.sin_addr = {
|
|
|
|
.s_addr = INADDR_ANY,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
struct sockaddr_in guest_addr = {
|
|
|
|
.sin_family = AF_INET,
|
|
|
|
.sin_addr = {
|
|
|
|
.s_addr = 0,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
int err;
|
2009-11-25 21:48:54 +03:00
|
|
|
int host_port, guest_port;
|
|
|
|
const char *p;
|
|
|
|
char buf[256];
|
|
|
|
int is_udp;
|
2021-09-26 00:48:20 +03:00
|
|
|
const char *end;
|
2017-09-08 18:53:59 +03:00
|
|
|
const char *fail_reason = "Unknown reason";
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
p = redir_str;
|
|
|
|
if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "No : separators";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
if (!strcmp(buf, "tcp") || buf[0] == '\0') {
|
|
|
|
is_udp = 0;
|
|
|
|
} else if (!strcmp(buf, "udp")) {
|
|
|
|
is_udp = 1;
|
|
|
|
} else {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Bad protocol name";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2018-08-22 16:43:30 +03:00
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
|
|
|
fail_reason = "Missing : separator";
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &host_addr.sin_addr)) {
|
2018-08-22 16:43:30 +03:00
|
|
|
fail_reason = "Bad host address";
|
|
|
|
goto fail_syntax;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
|
2018-08-22 16:43:30 +03:00
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Bad host port separator";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
err = qemu_strtoi(buf, &end, 0, &host_port);
|
|
|
|
if (err || host_port < 0 || host_port > 65535) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Bad host port";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
host_addr.sin_port = htons(host_port);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Missing guest address";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
if (buf[0] != '\0' && !inet_aton(buf, &guest_addr.sin_addr)) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Bad guest address";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2021-09-26 00:48:20 +03:00
|
|
|
err = qemu_strtoi(p, &end, 0, &guest_port);
|
|
|
|
if (err || guest_port < 1 || guest_port > 65535) {
|
2017-09-08 18:53:59 +03:00
|
|
|
fail_reason = "Bad guest port";
|
2009-11-25 21:48:54 +03:00
|
|
|
goto fail_syntax;
|
|
|
|
}
|
2021-09-26 00:48:20 +03:00
|
|
|
guest_addr.sin_port = htons(guest_port);
|
|
|
|
|
|
|
|
#if SLIRP_CHECK_VERSION(4, 5, 0)
|
|
|
|
err = slirp_add_hostxfwd(s->slirp,
|
|
|
|
(struct sockaddr *) &host_addr, sizeof(host_addr),
|
|
|
|
(struct sockaddr *) &guest_addr, sizeof(guest_addr),
|
|
|
|
is_udp ? SLIRP_HOSTFWD_UDP : 0);
|
|
|
|
#else
|
|
|
|
err = slirp_add_hostfwd(s->slirp, is_udp,
|
|
|
|
host_addr.sin_addr, host_port,
|
|
|
|
guest_addr.sin_addr, guest_port);
|
|
|
|
#endif
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2021-09-26 00:48:20 +03:00
|
|
|
if (err < 0) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Could not set up host forwarding rule '%s'",
|
|
|
|
redir_str);
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail_syntax:
|
2017-09-08 18:53:59 +03:00
|
|
|
error_setg(errp, "Invalid host forwarding rule '%s' (%s)", redir_str,
|
|
|
|
fail_reason);
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
hmp: Name HMP command handler functions hmp_COMMAND()
Some are called do_COMMAND() (old ones, usually), some hmp_COMMAND(),
and sometimes COMMAND pointlessly differs in spelling.
Normalize to hmp_COMMAND(), where COMMAND is exactly the command name
with '-' replaced by '_'.
Exceptions:
* do_device_add() and client_migrate_info() *not* renamed to
hmp_device_add(), hmp_client_migrate_info(), because they're also
QMP handlers. They still need to be converted to QAPI.
* do_memory_dump(), do_physical_memory_dump(), do_ioport_read(),
do_ioport_write() renamed do hmp_* instead of hmp_x(), hmp_xp(),
hmp_i(), hmp_o(), because those names are too cryptic for my taste.
* do_info_help() renamed to hmp_info_help() instead of hmp_info(),
because it only covers help.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-02-06 15:55:43 +03:00
|
|
|
void hmp_hostfwd_add(Monitor *mon, const QDict *qdict)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
|
|
|
const char *redir_str;
|
|
|
|
SlirpState *s;
|
|
|
|
const char *arg1 = qdict_get_str(qdict, "arg1");
|
|
|
|
const char *arg2 = qdict_get_try_str(qdict, "arg2");
|
|
|
|
|
2019-12-05 13:41:09 +03:00
|
|
|
if (arg2) {
|
|
|
|
s = slirp_lookup(mon, arg1);
|
2018-01-11 23:02:40 +03:00
|
|
|
redir_str = arg2;
|
2009-11-25 21:48:54 +03:00
|
|
|
} else {
|
2019-12-05 13:41:09 +03:00
|
|
|
s = slirp_lookup(mon, NULL);
|
2009-11-25 21:48:54 +03:00
|
|
|
redir_str = arg1;
|
|
|
|
}
|
|
|
|
if (s) {
|
2017-07-15 19:43:50 +03:00
|
|
|
Error *err = NULL;
|
2018-08-22 16:43:30 +03:00
|
|
|
if (slirp_hostfwd(s, redir_str, &err) < 0) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_report_err(err);
|
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-10-13 14:43:36 +03:00
|
|
|
#if defined(CONFIG_SMBD_COMMAND)
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
/* automatic user mode samba server configuration */
|
|
|
|
static void slirp_smb_cleanup(SlirpState *s)
|
|
|
|
{
|
2010-01-20 02:56:16 +03:00
|
|
|
int ret;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2017-04-07 17:32:54 +03:00
|
|
|
if (s->smb_dir) {
|
|
|
|
gchar *cmd = g_strdup_printf("rm -rf %s", s->smb_dir);
|
2010-01-20 02:56:16 +03:00
|
|
|
ret = system(cmd);
|
2010-03-04 12:00:31 +03:00
|
|
|
if (ret == -1 || !WIFEXITED(ret)) {
|
2010-02-18 19:25:24 +03:00
|
|
|
error_report("'%s' failed.", cmd);
|
2010-01-20 02:56:16 +03:00
|
|
|
} else if (WEXITSTATUS(ret)) {
|
2010-02-18 19:25:24 +03:00
|
|
|
error_report("'%s' failed. Error code: %d",
|
|
|
|
cmd, WEXITSTATUS(ret));
|
2010-01-20 02:56:16 +03:00
|
|
|
}
|
2017-04-07 17:32:54 +03:00
|
|
|
g_free(cmd);
|
|
|
|
g_free(s->smb_dir);
|
|
|
|
s->smb_dir = NULL;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int slirp_smb(SlirpState* s, const char *exported_dir,
|
2017-07-15 19:43:50 +03:00
|
|
|
struct in_addr vserver_addr, Error **errp)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2017-04-07 17:32:54 +03:00
|
|
|
char *smb_conf;
|
|
|
|
char *smb_cmdline;
|
2012-07-05 21:35:57 +04:00
|
|
|
struct passwd *passwd;
|
2009-11-25 21:48:54 +03:00
|
|
|
FILE *f;
|
|
|
|
|
2012-07-05 21:35:57 +04:00
|
|
|
passwd = getpwuid(geteuid());
|
|
|
|
if (!passwd) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Failed to retrieve user name");
|
2012-07-05 21:35:57 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-06 10:04:43 +04:00
|
|
|
if (access(CONFIG_SMBD_COMMAND, F_OK)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Could not find '%s', please install it",
|
|
|
|
CONFIG_SMBD_COMMAND);
|
2012-07-06 10:04:43 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(exported_dir, R_OK | X_OK)) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Error accessing shared directory '%s': %s",
|
|
|
|
exported_dir, strerror(errno));
|
2012-07-06 10:04:43 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-04-07 17:32:54 +03:00
|
|
|
s->smb_dir = g_dir_make_tmp("qemu-smb.XXXXXX", NULL);
|
|
|
|
if (!s->smb_dir) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Could not create samba server dir");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-07 17:32:54 +03:00
|
|
|
smb_conf = g_strdup_printf("%s/%s", s->smb_dir, "smb.conf");
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
f = fopen(smb_conf, "w");
|
|
|
|
if (!f) {
|
|
|
|
slirp_smb_cleanup(s);
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp,
|
|
|
|
"Could not create samba server configuration file '%s'",
|
|
|
|
smb_conf);
|
2017-04-07 17:32:54 +03:00
|
|
|
g_free(smb_conf);
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fprintf(f,
|
|
|
|
"[global]\n"
|
|
|
|
"private dir=%s\n"
|
slirp/smbd: modify/set several parameters in generated smbd.conf
The file sharing module should not handle printers, so disable it.
The options 'load printers' and 'printing' have been available since the
beginning (May 1996, commit 0e8fd3398771da2f016d72830179507f3edda51b).
Option 'disable spoolss' is available since Samba 2.0.4, commit
de5f42c9d9172592779fa2504d44544e3b6b1c0d).
Next, "socket address" was reported as deprecated, use a combination of
"interfaces" and "bind interfaces only" instead (available since October
1997, commit 79f4fb52c1ed56fd843f81b4eb0cdd2991d4d0f4).
Override cache directory to avoid writing to a global directory. Option
available since Samba 3.4.0, Jan 2009, commit
19a05bf2f485023b11b41dfae3f6459847d55ef7.
Set "usershare max shared=0" to prevent a global directory from being
used. Option available since Samba 3.0.23, February 2006, commit
5831715049f2d460ce42299963a5defdc160891b.
The last option was introduced with Samba 3.4.0, but previously
"state directory" was already added which exists in Samba 3.4.0. As
unknown parameters are ignored (while printing a warning), it should be
safe to add another option.
Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2014-11-03 13:52:10 +03:00
|
|
|
"interfaces=127.0.0.1\n"
|
|
|
|
"bind interfaces only=yes\n"
|
2009-11-25 21:48:54 +03:00
|
|
|
"pid directory=%s\n"
|
|
|
|
"lock directory=%s\n"
|
2012-04-25 17:57:19 +04:00
|
|
|
"state directory=%s\n"
|
slirp/smbd: modify/set several parameters in generated smbd.conf
The file sharing module should not handle printers, so disable it.
The options 'load printers' and 'printing' have been available since the
beginning (May 1996, commit 0e8fd3398771da2f016d72830179507f3edda51b).
Option 'disable spoolss' is available since Samba 2.0.4, commit
de5f42c9d9172592779fa2504d44544e3b6b1c0d).
Next, "socket address" was reported as deprecated, use a combination of
"interfaces" and "bind interfaces only" instead (available since October
1997, commit 79f4fb52c1ed56fd843f81b4eb0cdd2991d4d0f4).
Override cache directory to avoid writing to a global directory. Option
available since Samba 3.4.0, Jan 2009, commit
19a05bf2f485023b11b41dfae3f6459847d55ef7.
Set "usershare max shared=0" to prevent a global directory from being
used. Option available since Samba 3.0.23, February 2006, commit
5831715049f2d460ce42299963a5defdc160891b.
The last option was introduced with Samba 3.4.0, but previously
"state directory" was already added which exists in Samba 3.4.0. As
unknown parameters are ignored (while printing a warning), it should be
safe to add another option.
Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2014-11-03 13:52:10 +03:00
|
|
|
"cache directory=%s\n"
|
2014-04-27 14:54:12 +04:00
|
|
|
"ncalrpc dir=%s/ncalrpc\n"
|
2009-11-25 21:48:54 +03:00
|
|
|
"log file=%s/log.smbd\n"
|
|
|
|
"smb passwd file=%s/smbpasswd\n"
|
2013-11-01 15:23:49 +04:00
|
|
|
"security = user\n"
|
|
|
|
"map to guest = Bad User\n"
|
slirp/smbd: modify/set several parameters in generated smbd.conf
The file sharing module should not handle printers, so disable it.
The options 'load printers' and 'printing' have been available since the
beginning (May 1996, commit 0e8fd3398771da2f016d72830179507f3edda51b).
Option 'disable spoolss' is available since Samba 2.0.4, commit
de5f42c9d9172592779fa2504d44544e3b6b1c0d).
Next, "socket address" was reported as deprecated, use a combination of
"interfaces" and "bind interfaces only" instead (available since October
1997, commit 79f4fb52c1ed56fd843f81b4eb0cdd2991d4d0f4).
Override cache directory to avoid writing to a global directory. Option
available since Samba 3.4.0, Jan 2009, commit
19a05bf2f485023b11b41dfae3f6459847d55ef7.
Set "usershare max shared=0" to prevent a global directory from being
used. Option available since Samba 3.0.23, February 2006, commit
5831715049f2d460ce42299963a5defdc160891b.
The last option was introduced with Samba 3.4.0, but previously
"state directory" was already added which exists in Samba 3.4.0. As
unknown parameters are ignored (while printing a warning), it should be
safe to add another option.
Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2014-11-03 13:52:10 +03:00
|
|
|
"load printers = no\n"
|
|
|
|
"printing = bsd\n"
|
|
|
|
"disable spoolss = yes\n"
|
|
|
|
"usershare max shares = 0\n"
|
2009-11-25 21:48:54 +03:00
|
|
|
"[qemu]\n"
|
|
|
|
"path=%s\n"
|
|
|
|
"read only=no\n"
|
2012-07-05 21:35:57 +04:00
|
|
|
"guest ok=yes\n"
|
|
|
|
"force user=%s\n",
|
2009-11-25 21:48:54 +03:00
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
|
|
|
s->smb_dir,
|
2012-04-25 17:57:19 +04:00
|
|
|
s->smb_dir,
|
2014-04-27 14:54:12 +04:00
|
|
|
s->smb_dir,
|
slirp/smbd: modify/set several parameters in generated smbd.conf
The file sharing module should not handle printers, so disable it.
The options 'load printers' and 'printing' have been available since the
beginning (May 1996, commit 0e8fd3398771da2f016d72830179507f3edda51b).
Option 'disable spoolss' is available since Samba 2.0.4, commit
de5f42c9d9172592779fa2504d44544e3b6b1c0d).
Next, "socket address" was reported as deprecated, use a combination of
"interfaces" and "bind interfaces only" instead (available since October
1997, commit 79f4fb52c1ed56fd843f81b4eb0cdd2991d4d0f4).
Override cache directory to avoid writing to a global directory. Option
available since Samba 3.4.0, Jan 2009, commit
19a05bf2f485023b11b41dfae3f6459847d55ef7.
Set "usershare max shared=0" to prevent a global directory from being
used. Option available since Samba 3.0.23, February 2006, commit
5831715049f2d460ce42299963a5defdc160891b.
The last option was introduced with Samba 3.4.0, but previously
"state directory" was already added which exists in Samba 3.4.0. As
unknown parameters are ignored (while printing a warning), it should be
safe to add another option.
Signed-off-by: Peter Wu <peter@lekensteyn.nl>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2014-11-03 13:52:10 +03:00
|
|
|
s->smb_dir,
|
2012-07-05 21:35:57 +04:00
|
|
|
exported_dir,
|
|
|
|
passwd->pw_name
|
2009-11-25 21:48:54 +03:00
|
|
|
);
|
|
|
|
fclose(f);
|
|
|
|
|
2017-04-07 17:32:54 +03:00
|
|
|
smb_cmdline = g_strdup_printf("%s -l %s -s %s",
|
2014-10-25 00:29:50 +04:00
|
|
|
CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf);
|
2017-04-07 17:32:54 +03:00
|
|
|
g_free(smb_conf);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2019-01-17 14:43:33 +03:00
|
|
|
if (slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 139) < 0 ||
|
|
|
|
slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 445) < 0) {
|
2009-11-25 21:48:54 +03:00
|
|
|
slirp_smb_cleanup(s);
|
2017-04-07 17:32:54 +03:00
|
|
|
g_free(smb_cmdline);
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Conflicting/invalid smbserver address");
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
2017-04-07 17:32:54 +03:00
|
|
|
g_free(smb_cmdline);
|
2009-11-25 21:48:54 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-13 14:43:36 +03:00
|
|
|
#endif /* defined(CONFIG_SMBD_COMMAND) */
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:43:54 +03:00
|
|
|
static ssize_t guestfwd_write(const void *buf, size_t len, void *chr)
|
2019-01-17 14:43:33 +03:00
|
|
|
{
|
|
|
|
return qemu_chr_fe_write_all(chr, buf, len);
|
|
|
|
}
|
|
|
|
|
2018-08-22 16:43:30 +03:00
|
|
|
static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2016-04-01 01:46:35 +03:00
|
|
|
/* TODO: IPv6 */
|
2009-11-25 21:48:54 +03:00
|
|
|
struct in_addr server = { .s_addr = 0 };
|
|
|
|
struct GuestFwd *fwd;
|
|
|
|
const char *p;
|
|
|
|
char buf[128];
|
|
|
|
char *end;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
p = config_str;
|
2018-08-22 16:43:30 +03:00
|
|
|
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;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
port = strtol(buf, &end, 10);
|
|
|
|
if (*end != '\0' || port < 1 || port > 65535) {
|
|
|
|
goto fail_syntax;
|
|
|
|
}
|
|
|
|
|
2011-06-04 09:25:59 +04:00
|
|
|
snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2019-01-17 14:43:34 +03:00
|
|
|
if (g_str_has_prefix(p, "cmd:")) {
|
2019-01-17 14:43:33 +03:00
|
|
|
if (slirp_add_exec(s->slirp, &p[4], &server, port) < 0) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Conflicting/invalid host:port in guest "
|
|
|
|
"forwarding rule '%s'", config_str);
|
2012-06-03 11:45:01 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2016-10-22 12:52:52 +03:00
|
|
|
Error *err = NULL;
|
chardev: mark the calls that allow an implicit mux monitor
This is mostly for readability of the code. Let's make it clear which
callers can create an implicit monitor when the chardev is muxed.
This will also enforce a safer behaviour, as we don't really support
creating monitor anywhere/anytime at the moment. Add an assert() to
make sure the programmer explicitely wanted that behaviour.
There are documented cases, such as: -serial/-parallel/-virtioconsole
and to less extent -debugcon.
Less obvious and questionable ones are -gdb, SLIRP -guestfwd and Xen
console. Add a FIXME note for those, but keep the support for now.
Other qemu_chr_new() callers either have a fixed parameter/filename
string or do not need it, such as -qtest:
* qtest.c: qtest_init()
Afaik, only used by tests/libqtest.c, without mux. I don't think we
support it outside of qemu testing: drop support for implicit mux
monitor (qemu_chr_new() call: no implicit mux now).
* hw/
All with literal @filename argument that doesn't enable mux monitor.
* tests/
All with @filename argument that doesn't enable mux monitor.
On a related note, the list of monitor creation places:
- the chardev creators listed above: all from command line (except
perhaps Xen console?)
- -gdb & hmp gdbserver will create a "GDB monitor command" chardev
that is wired to an HMP monitor.
- -mon command line option
From this short study, I would like to think that a monitor may only
be created in the main thread today, though I remain skeptical :)
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
2018-08-22 20:19:42 +03:00
|
|
|
/*
|
|
|
|
* FIXME: sure we want to support implicit
|
|
|
|
* muxed monitors here?
|
|
|
|
*/
|
2019-02-13 16:18:13 +03:00
|
|
|
Chardev *chr = qemu_chr_new_mux_mon(buf, p, NULL);
|
2016-10-22 12:52:52 +03:00
|
|
|
|
|
|
|
if (!chr) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Could not open guest forwarding device '%s'",
|
|
|
|
buf);
|
2016-10-22 12:52:52 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fwd = g_new(struct GuestFwd, 1);
|
|
|
|
qemu_chr_fe_init(&fwd->hd, chr, &err);
|
|
|
|
if (err) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_propagate(errp, err);
|
2019-01-17 14:43:36 +03:00
|
|
|
object_unparent(OBJECT(chr));
|
2012-06-03 11:45:01 +04:00
|
|
|
g_free(fwd);
|
|
|
|
return -1;
|
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2019-01-17 14:43:33 +03:00
|
|
|
if (slirp_add_guestfwd(s->slirp, guestfwd_write, &fwd->hd,
|
|
|
|
&server, port) < 0) {
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Conflicting/invalid host:port in guest "
|
|
|
|
"forwarding rule '%s'", config_str);
|
2019-01-17 14:43:36 +03:00
|
|
|
qemu_chr_fe_deinit(&fwd->hd, true);
|
2012-06-03 11:45:01 +04:00
|
|
|
g_free(fwd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
fwd->server = server;
|
|
|
|
fwd->port = port;
|
|
|
|
fwd->slirp = s->slirp;
|
|
|
|
|
2016-10-22 12:52:55 +03:00
|
|
|
qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read,
|
2017-07-06 15:08:49 +03:00
|
|
|
NULL, NULL, fwd, NULL, true);
|
2019-01-17 14:43:35 +03:00
|
|
|
s->fwd = g_slist_append(s->fwd, fwd);
|
2012-06-03 11:45:01 +04:00
|
|
|
}
|
2009-11-25 21:48:54 +03:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail_syntax:
|
2017-07-15 19:43:50 +03:00
|
|
|
error_setg(errp, "Invalid guest forwarding rule '%s'", config_str);
|
2009-11-25 21:48:54 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-02-06 16:18:24 +03:00
|
|
|
void hmp_info_usernet(Monitor *mon, const QDict *qdict)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
|
|
|
SlirpState *s;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(s, &slirp_stacks, entry) {
|
2012-07-24 19:35:06 +04:00
|
|
|
int id;
|
2018-04-30 21:02:24 +03:00
|
|
|
bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0;
|
2018-11-10 16:45:43 +03:00
|
|
|
char *info = slirp_connection_info(s->slirp);
|
|
|
|
monitor_printf(mon, "Hub %d (%s):\n%s",
|
2018-04-30 21:02:24 +03:00
|
|
|
got_hub_id ? id : -1,
|
2018-11-10 16:45:43 +03:00
|
|
|
s->nc.name, info);
|
|
|
|
g_free(info);
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:16 +04:00
|
|
|
static void
|
|
|
|
net_init_slirp_configs(const StringList *fwd, int flags)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
2012-07-17 18:17:16 +04:00
|
|
|
while (fwd) {
|
|
|
|
struct slirp_config_str *config;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2012-07-17 18:17:16 +04:00
|
|
|
config = g_malloc0(sizeof(*config));
|
|
|
|
pstrcpy(config->str, sizeof(config->str), fwd->value->str);
|
|
|
|
config->flags = flags;
|
|
|
|
config->next = slirp_configs;
|
|
|
|
slirp_configs = config;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2012-07-17 18:17:16 +04:00
|
|
|
fwd = fwd->next;
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-27 21:53:39 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-14 06:50:12 +03:00
|
|
|
int net_init_slirp(const Netdev *netdev, const char *name,
|
2015-05-15 14:58:50 +03:00
|
|
|
NetClientState *peer, Error **errp)
|
2009-11-25 21:48:54 +03:00
|
|
|
{
|
|
|
|
struct slirp_config_str *config;
|
2012-07-17 18:17:16 +04:00
|
|
|
char *vnet;
|
2009-11-25 21:48:54 +03:00
|
|
|
int ret;
|
2012-07-17 18:17:16 +04:00
|
|
|
const NetdevUserOptions *user;
|
2012-10-27 21:53:39 +04:00
|
|
|
const char **dnssearch;
|
2016-03-20 14:29:54 +03:00
|
|
|
bool ipv4 = true, ipv6 = true;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
qapi: Change Netdev into a flat union
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>
2016-07-14 06:50:23 +03:00
|
|
|
assert(netdev->type == NET_CLIENT_DRIVER_USER);
|
|
|
|
user = &netdev->u.user;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2016-03-20 14:29:54 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-11-04 19:07:00 +03:00
|
|
|
vnet = user->net ? g_strdup(user->net) :
|
|
|
|
user->ip ? g_strdup_printf("%s/24", user->ip) :
|
2012-07-17 18:17:16 +04:00
|
|
|
NULL;
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2012-10-27 21:53:39 +04:00
|
|
|
dnssearch = slirp_dnssearch(user->dnssearch);
|
|
|
|
|
2012-07-17 18:17:16 +04:00
|
|
|
/* all optional fields are initialized to "all bits zero" */
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2012-07-17 18:17:16 +04:00
|
|
|
net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
|
|
|
|
net_init_slirp_configs(user->guestfwd, 0);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
2016-03-20 14:29:54 +03:00
|
|
|
ret = net_slirp_init(peer, "user", name, user->q_restrict,
|
|
|
|
ipv4, vnet, user->host,
|
|
|
|
ipv6, user->ipv6_prefix, user->ipv6_prefixlen,
|
2016-03-25 02:02:58 +03:00
|
|
|
user->ipv6_host, user->hostname, user->tftp,
|
2016-03-15 12:31:22 +03:00
|
|
|
user->bootfile, user->dhcpstart,
|
2016-03-25 02:02:58 +03:00
|
|
|
user->dns, user->ipv6_dns, user->smb,
|
2018-09-14 10:26:16 +03:00
|
|
|
user->smbserver, dnssearch, user->domainname,
|
|
|
|
user->tftp_server_name, errp);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
while (slirp_configs) {
|
|
|
|
config = slirp_configs;
|
|
|
|
slirp_configs = config->next;
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(config);
|
2009-11-25 21:48:54 +03:00
|
|
|
}
|
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(vnet);
|
2012-10-27 21:53:39 +04:00
|
|
|
g_free(dnssearch);
|
2009-11-25 21:48:54 +03:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|