mirror of
https://github.com/mii443/qemu.git
synced 2025-08-22 23:25:48 +00:00
Now that all front end use qemu_chr_fe_init(), we can move chardev claiming in init(), and add a function deinit() to release the chardev and cleanup handlers. The qemu_chr_fe_claim_no_fail() for property are gone, since the property will raise an error instead. In other cases, where there is already an error path, an error is raised instead. Finally, other cases are handled by &error_abort in qemu_chr_fe_init(). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20161022095318.17775-19-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
392 lines
10 KiB
C
392 lines
10 KiB
C
/*
|
|
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
|
* Copyright (c) 2016 FUJITSU LIMITED
|
|
* Copyright (c) 2016 Intel Corporation
|
|
*
|
|
* Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or
|
|
* later. See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "net/filter.h"
|
|
#include "net/net.h"
|
|
#include "qemu-common.h"
|
|
#include "qapi/error.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
#include "qapi-visit.h"
|
|
#include "qom/object.h"
|
|
#include "qemu/main-loop.h"
|
|
#include "qemu/error-report.h"
|
|
#include "trace.h"
|
|
#include "sysemu/char.h"
|
|
#include "qemu/iov.h"
|
|
#include "qemu/sockets.h"
|
|
|
|
#define FILTER_MIRROR(obj) \
|
|
OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_MIRROR)
|
|
|
|
#define FILTER_REDIRECTOR(obj) \
|
|
OBJECT_CHECK(MirrorState, (obj), TYPE_FILTER_REDIRECTOR)
|
|
|
|
#define TYPE_FILTER_MIRROR "filter-mirror"
|
|
#define TYPE_FILTER_REDIRECTOR "filter-redirector"
|
|
#define REDIRECTOR_MAX_LEN NET_BUFSIZE
|
|
|
|
typedef struct MirrorState {
|
|
NetFilterState parent_obj;
|
|
char *indev;
|
|
char *outdev;
|
|
CharBackend chr_in;
|
|
CharBackend chr_out;
|
|
SocketReadState rs;
|
|
} MirrorState;
|
|
|
|
static int filter_mirror_send(CharBackend *chr_out,
|
|
const struct iovec *iov,
|
|
int iovcnt)
|
|
{
|
|
int ret = 0;
|
|
ssize_t size = 0;
|
|
uint32_t len = 0;
|
|
char *buf;
|
|
|
|
size = iov_size(iov, iovcnt);
|
|
if (!size) {
|
|
return 0;
|
|
}
|
|
|
|
len = htonl(size);
|
|
ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)&len, sizeof(len));
|
|
if (ret != sizeof(len)) {
|
|
goto err;
|
|
}
|
|
|
|
buf = g_malloc(size);
|
|
iov_to_buf(iov, iovcnt, 0, buf, size);
|
|
ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)buf, size);
|
|
g_free(buf);
|
|
if (ret != size) {
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
return ret < 0 ? ret : -EIO;
|
|
}
|
|
|
|
static void
|
|
redirector_to_filter(NetFilterState *nf, const uint8_t *buf, int len)
|
|
{
|
|
struct iovec iov = {
|
|
.iov_base = (void *)buf,
|
|
.iov_len = len,
|
|
};
|
|
|
|
if (nf->direction == NET_FILTER_DIRECTION_ALL ||
|
|
nf->direction == NET_FILTER_DIRECTION_TX) {
|
|
qemu_netfilter_pass_to_next(nf->netdev, 0, &iov, 1, nf);
|
|
}
|
|
|
|
if (nf->direction == NET_FILTER_DIRECTION_ALL ||
|
|
nf->direction == NET_FILTER_DIRECTION_RX) {
|
|
qemu_netfilter_pass_to_next(nf->netdev->peer, 0, &iov, 1, nf);
|
|
}
|
|
}
|
|
|
|
static int redirector_chr_can_read(void *opaque)
|
|
{
|
|
return REDIRECTOR_MAX_LEN;
|
|
}
|
|
|
|
static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
|
|
{
|
|
NetFilterState *nf = opaque;
|
|
MirrorState *s = FILTER_REDIRECTOR(nf);
|
|
int ret;
|
|
|
|
ret = net_fill_rstate(&s->rs, buf, size);
|
|
|
|
if (ret == -1) {
|
|
qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void redirector_chr_event(void *opaque, int event)
|
|
{
|
|
NetFilterState *nf = opaque;
|
|
MirrorState *s = FILTER_REDIRECTOR(nf);
|
|
|
|
switch (event) {
|
|
case CHR_EVENT_CLOSED:
|
|
qemu_chr_fe_set_handlers(&s->chr_in, NULL, NULL, NULL, NULL, NULL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static ssize_t filter_mirror_receive_iov(NetFilterState *nf,
|
|
NetClientState *sender,
|
|
unsigned flags,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
NetPacketSent *sent_cb)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(nf);
|
|
int ret;
|
|
|
|
ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
|
|
if (ret) {
|
|
error_report("filter_mirror_send failed(%s)", strerror(-ret));
|
|
}
|
|
|
|
/*
|
|
* we don't hope this error interrupt the normal
|
|
* path of net packet, so we always return zero.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
|
|
NetClientState *sender,
|
|
unsigned flags,
|
|
const struct iovec *iov,
|
|
int iovcnt,
|
|
NetPacketSent *sent_cb)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(nf);
|
|
int ret;
|
|
|
|
if (qemu_chr_fe_get_driver(&s->chr_out)) {
|
|
ret = filter_mirror_send(&s->chr_out, iov, iovcnt);
|
|
if (ret) {
|
|
error_report("filter_mirror_send failed(%s)", strerror(-ret));
|
|
}
|
|
return iov_size(iov, iovcnt);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void filter_mirror_cleanup(NetFilterState *nf)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(nf);
|
|
|
|
qemu_chr_fe_deinit(&s->chr_out);
|
|
}
|
|
|
|
static void filter_redirector_cleanup(NetFilterState *nf)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(nf);
|
|
|
|
qemu_chr_fe_deinit(&s->chr_in);
|
|
qemu_chr_fe_deinit(&s->chr_out);
|
|
}
|
|
|
|
static void filter_mirror_setup(NetFilterState *nf, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(nf);
|
|
CharDriverState *chr;
|
|
|
|
if (!s->outdev) {
|
|
error_setg(errp, "filter mirror needs 'outdev' "
|
|
"property set");
|
|
return;
|
|
}
|
|
|
|
chr = qemu_chr_find(s->outdev);
|
|
if (chr == NULL) {
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
"Device '%s' not found", s->outdev);
|
|
return;
|
|
}
|
|
|
|
qemu_chr_fe_init(&s->chr_out, chr, errp);
|
|
}
|
|
|
|
static void redirector_rs_finalize(SocketReadState *rs)
|
|
{
|
|
MirrorState *s = container_of(rs, MirrorState, rs);
|
|
NetFilterState *nf = NETFILTER(s);
|
|
|
|
redirector_to_filter(nf, rs->buf, rs->packet_len);
|
|
}
|
|
|
|
static void filter_redirector_setup(NetFilterState *nf, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(nf);
|
|
CharDriverState *chr;
|
|
|
|
if (!s->indev && !s->outdev) {
|
|
error_setg(errp, "filter redirector needs 'indev' or "
|
|
"'outdev' at least one property set");
|
|
return;
|
|
} else if (s->indev && s->outdev) {
|
|
if (!strcmp(s->indev, s->outdev)) {
|
|
error_setg(errp, "'indev' and 'outdev' could not be same "
|
|
"for filter redirector");
|
|
return;
|
|
}
|
|
}
|
|
|
|
net_socket_rs_init(&s->rs, redirector_rs_finalize);
|
|
|
|
if (s->indev) {
|
|
chr = qemu_chr_find(s->indev);
|
|
if (chr == NULL) {
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
"IN Device '%s' not found", s->indev);
|
|
return;
|
|
}
|
|
|
|
if (!qemu_chr_fe_init(&s->chr_in, chr, errp)) {
|
|
return;
|
|
}
|
|
|
|
qemu_chr_fe_set_handlers(&s->chr_in, redirector_chr_can_read,
|
|
redirector_chr_read, redirector_chr_event,
|
|
nf, NULL);
|
|
}
|
|
|
|
if (s->outdev) {
|
|
chr = qemu_chr_find(s->outdev);
|
|
if (chr == NULL) {
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
"OUT Device '%s' not found", s->outdev);
|
|
return;
|
|
}
|
|
if (!qemu_chr_fe_init(&s->chr_out, chr, errp)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void filter_mirror_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
NetFilterClass *nfc = NETFILTER_CLASS(oc);
|
|
|
|
nfc->setup = filter_mirror_setup;
|
|
nfc->cleanup = filter_mirror_cleanup;
|
|
nfc->receive_iov = filter_mirror_receive_iov;
|
|
}
|
|
|
|
static void filter_redirector_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
NetFilterClass *nfc = NETFILTER_CLASS(oc);
|
|
|
|
nfc->setup = filter_redirector_setup;
|
|
nfc->cleanup = filter_redirector_cleanup;
|
|
nfc->receive_iov = filter_redirector_receive_iov;
|
|
}
|
|
|
|
static char *filter_redirector_get_indev(Object *obj, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(obj);
|
|
|
|
return g_strdup(s->indev);
|
|
}
|
|
|
|
static void
|
|
filter_redirector_set_indev(Object *obj, const char *value, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(obj);
|
|
|
|
g_free(s->indev);
|
|
s->indev = g_strdup(value);
|
|
}
|
|
|
|
static char *filter_mirror_get_outdev(Object *obj, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(obj);
|
|
|
|
return g_strdup(s->outdev);
|
|
}
|
|
|
|
static void
|
|
filter_mirror_set_outdev(Object *obj, const char *value, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(obj);
|
|
|
|
g_free(s->outdev);
|
|
s->outdev = g_strdup(value);
|
|
if (!s->outdev) {
|
|
error_setg(errp, "filter mirror needs 'outdev' "
|
|
"property set");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static char *filter_redirector_get_outdev(Object *obj, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(obj);
|
|
|
|
return g_strdup(s->outdev);
|
|
}
|
|
|
|
static void
|
|
filter_redirector_set_outdev(Object *obj, const char *value, Error **errp)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(obj);
|
|
|
|
g_free(s->outdev);
|
|
s->outdev = g_strdup(value);
|
|
}
|
|
|
|
static void filter_mirror_init(Object *obj)
|
|
{
|
|
object_property_add_str(obj, "outdev", filter_mirror_get_outdev,
|
|
filter_mirror_set_outdev, NULL);
|
|
}
|
|
|
|
static void filter_redirector_init(Object *obj)
|
|
{
|
|
object_property_add_str(obj, "indev", filter_redirector_get_indev,
|
|
filter_redirector_set_indev, NULL);
|
|
object_property_add_str(obj, "outdev", filter_redirector_get_outdev,
|
|
filter_redirector_set_outdev, NULL);
|
|
}
|
|
|
|
static void filter_mirror_fini(Object *obj)
|
|
{
|
|
MirrorState *s = FILTER_MIRROR(obj);
|
|
|
|
g_free(s->outdev);
|
|
}
|
|
|
|
static void filter_redirector_fini(Object *obj)
|
|
{
|
|
MirrorState *s = FILTER_REDIRECTOR(obj);
|
|
|
|
g_free(s->indev);
|
|
g_free(s->outdev);
|
|
}
|
|
|
|
static const TypeInfo filter_redirector_info = {
|
|
.name = TYPE_FILTER_REDIRECTOR,
|
|
.parent = TYPE_NETFILTER,
|
|
.class_init = filter_redirector_class_init,
|
|
.instance_init = filter_redirector_init,
|
|
.instance_finalize = filter_redirector_fini,
|
|
.instance_size = sizeof(MirrorState),
|
|
};
|
|
|
|
static const TypeInfo filter_mirror_info = {
|
|
.name = TYPE_FILTER_MIRROR,
|
|
.parent = TYPE_NETFILTER,
|
|
.class_init = filter_mirror_class_init,
|
|
.instance_init = filter_mirror_init,
|
|
.instance_finalize = filter_mirror_fini,
|
|
.instance_size = sizeof(MirrorState),
|
|
};
|
|
|
|
static void register_types(void)
|
|
{
|
|
type_register_static(&filter_mirror_info);
|
|
type_register_static(&filter_redirector_info);
|
|
}
|
|
|
|
type_init(register_types);
|