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

Version: GnuPG v1
 
 iQEcBAABAgAGBQJW+ybDAAoJEO8Ells5jWIRdocH/3xh1XQp9McwEsflQxwr/Gk4
 bgJaYRrXCMxU6e9vQPXIfL54V0rzS381GmiZMDPg+OmRoEFyHwxN3cIeuKmAjAEG
 VH2iMLLpLMMPyQFpQmrimwsFx8j9drG3dFywrkoZkhhKOlWhYZwI3sqQgMfCC1nR
 ul6KqBjKNK81SarsbdEypynucQtSxuT9XkC+h4QbjpswOadpjHUKP04fEa7mej9e
 boK0aMgiDW/96XCb9xZMya03Lea8a4i1V7Hk2u3xtWgBjEtiZMx5ZH9SnTMQI4Ga
 cj0px4UphnuvQp31ACBAZIEyTxyDUZzRvgoUhaZRwIsDaS9uAKRlDUO0UxBlKXY=
 =E2Vx
 -----END PGP SIGNATURE-----

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

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

* remotes/jasowang/tags/net-pull-request:
  Revert "e1000: fix hang of win2k12 shutdown with flood ping"
  e1000: Fixing interrupts pace.
  tests/test-filter-redirector: Add unit test for filter-redirector
  net/filter-mirror: implement filter-redirector
  net/filter-mirror: Change filter_mirror_send interface
  tests/test-filter-mirror:add filter-mirror unit test
  net/filter-mirror:Add filter-mirror

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-03-30 12:30:38 +01:00
commit 8850dcbfd7
9 changed files with 773 additions and 6 deletions

View File

@ -357,6 +357,14 @@ set_interrupt_cause(E1000State *s, int index, uint32_t val)
}
mit_update_delay(&mit_delay, s->mac_reg[ITR]);
/*
* According to e1000 SPEC, the Ethernet controller guarantees
* a maximum observable interrupt rate of 7813 interrupts/sec.
* Thus if mit_delay < 500 then the delay should be set to the
* minimum delay possible which is 500.
*/
mit_delay = (mit_delay < 500) ? 500 : mit_delay;
if (mit_delay) {
s->mit_timer_on = 1;
timer_mod(s->mit_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
@ -448,11 +456,6 @@ static void e1000_reset(void *opaque)
e1000_link_down(d);
}
/* Throttle interrupts to prevent guest (e.g Win 2012) from
* reinjecting interrupts endlessly. TODO: fix non ITR case.
*/
d->mac_reg[ITR] = 250;
/* Some guests expect pre-initialized RAH/RAL (AddrValid flag + MACaddr) */
d->mac_reg[RA] = 0;
d->mac_reg[RA + 1] = E1000_RAH_AV;

View File

@ -15,3 +15,4 @@ common-obj-$(CONFIG_VDE) += vde.o
common-obj-$(CONFIG_NETMAP) += netmap.o
common-obj-y += filter.o
common-obj-y += filter-buffer.o
common-obj-y += filter-mirror.o

427
net/filter-mirror.c Normal file
View File

@ -0,0 +1,427 @@
/*
* 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;
CharDriverState *chr_in;
CharDriverState *chr_out;
int state; /* 0 = getting length, 1 = getting data */
unsigned int index;
unsigned int packet_len;
uint8_t buf[REDIRECTOR_MAX_LEN];
} MirrorState;
static int filter_mirror_send(CharDriverState *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);
unsigned int l;
while (size > 0) {
/* reassemble a packet from the network */
switch (s->state) { /* 0 = getting length, 1 = getting data */
case 0:
l = 4 - s->index;
if (l > size) {
l = size;
}
memcpy(s->buf + s->index, buf, l);
buf += l;
size -= l;
s->index += l;
if (s->index == 4) {
/* got length */
s->packet_len = ntohl(*(uint32_t *)s->buf);
s->index = 0;
s->state = 1;
}
break;
case 1:
l = s->packet_len - s->index;
if (l > size) {
l = size;
}
if (s->index + l <= sizeof(s->buf)) {
memcpy(s->buf + s->index, buf, l);
} else {
error_report("serious error: oversized packet received.");
s->index = s->state = 0;
qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
return;
}
s->index += l;
buf += l;
size -= l;
if (s->index >= s->packet_len) {
s->index = 0;
s->state = 0;
redirector_to_filter(nf, s->buf, s->packet_len);
}
break;
}
}
}
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_add_handlers(s->chr_in, 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 (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);
if (s->chr_out) {
qemu_chr_fe_release(s->chr_out);
}
}
static void filter_redirector_cleanup(NetFilterState *nf)
{
MirrorState *s = FILTER_REDIRECTOR(nf);
if (s->chr_in) {
qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
qemu_chr_fe_release(s->chr_in);
}
if (s->chr_out) {
qemu_chr_fe_release(s->chr_out);
}
}
static void filter_mirror_setup(NetFilterState *nf, Error **errp)
{
MirrorState *s = FILTER_MIRROR(nf);
if (!s->outdev) {
error_setg(errp, "filter filter mirror needs 'outdev' "
"property set");
return;
}
s->chr_out = qemu_chr_find(s->outdev);
if (s->chr_out == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", s->outdev);
return;
}
if (qemu_chr_fe_claim(s->chr_out) != 0) {
error_setg(errp, QERR_DEVICE_IN_USE, s->outdev);
return;
}
}
static void filter_redirector_setup(NetFilterState *nf, Error **errp)
{
MirrorState *s = FILTER_REDIRECTOR(nf);
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;
}
}
s->state = s->index = 0;
if (s->indev) {
s->chr_in = qemu_chr_find(s->indev);
if (s->chr_in == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"IN Device '%s' not found", s->indev);
return;
}
qemu_chr_fe_claim_no_fail(s->chr_in);
qemu_chr_add_handlers(s->chr_in, redirector_chr_can_read,
redirector_chr_read, redirector_chr_event, nf);
}
if (s->outdev) {
s->chr_out = qemu_chr_find(s->outdev);
if (s->chr_out == NULL) {
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
"OUT Device '%s' not found", s->outdev);
return;
}
qemu_chr_fe_claim_no_fail(s->chr_out);
}
}
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 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);

View File

@ -3841,6 +3841,20 @@ queue @var{all|rx|tx} is an option that can be applied to any netfilter.
@option{tx}: the filter is attached to the transmit queue of the netdev,
where it will receive packets sent by the netdev.
@item -object filter-mirror,id=@var{id},netdev=@var{netdevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
filter-mirror on netdev @var{netdevid},mirror net packet to chardev
@var{chardevid}
@item -object filter-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},
outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
filter-redirector on netdev @var{netdevid},redirect filter's net packet to chardev
@var{chardevid},and redirect indev's packet to filter.
Create a filter-redirector we need to differ outdev id from indev id, id can not
be the same. we can just use indev or outdev, but at least one of indev or outdev
need to be specified.
@item -object filter-dump,id=@var{id},netdev=@var{dev},file=@var{filename}][,maxlen=@var{len}]
Dump the network traffic on netdev @var{dev} to the file specified by

2
tests/.gitignore vendored
View File

@ -68,5 +68,7 @@ test-write-threshold
test-x86-cpuid
test-xbzrle
test-netfilter
test-filter-mirror
test-filter-redirector
*-test
qapi-schema/*.test.*

View File

@ -220,6 +220,8 @@ ifeq ($(CONFIG_VHOST_NET_TEST_i386),)
check-qtest-x86_64-$(CONFIG_VHOST_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF)
endif
check-qtest-i386-y += tests/test-netfilter$(EXESUF)
check-qtest-i386-y += tests/test-filter-mirror$(EXESUF)
check-qtest-i386-y += tests/test-filter-redirector$(EXESUF)
check-qtest-x86_64-y = $(check-qtest-i386-y)
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
@ -580,6 +582,8 @@ tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_hel
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y)
tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y)
tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y)
tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y)
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o

View File

@ -0,0 +1,93 @@
/*
* QTest testcase for filter-mirror
*
* Copyright (c) 2016 FUJITSU LIMITED
* 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 <glib.h>
#include "libqtest.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
static void test_mirror(void)
{
#ifndef _WIN32
/* socketpair(PF_UNIX) which does not exist on windows */
int send_sock[2], recv_sock;
char *cmdline;
uint32_t ret = 0, len = 0;
char send_buf[] = "Hello! filter-mirror~";
char sock_path[] = "filter-mirror.XXXXXX";
char *recv_buf;
uint32_t size = sizeof(send_buf);
size = htonl(size);
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, send_sock);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path);
g_assert_cmpint(ret, !=, -1);
cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d "
"-device e1000,netdev=qtest-bn0,id=qtest-e0 "
"-chardev socket,id=mirror0,path=%s,server,nowait "
"-object filter-mirror,id=qtest-f0,netdev=qtest-bn0,queue=tx,outdev=mirror0 "
, send_sock[1], sock_path);
qtest_start(cmdline);
g_free(cmdline);
recv_sock = unix_connect(sock_path, NULL);
g_assert_cmpint(recv_sock, !=, -1);
struct iovec iov[] = {
{
.iov_base = &size,
.iov_len = sizeof(size),
}, {
.iov_base = send_buf,
.iov_len = sizeof(send_buf),
},
};
/* send a qmp command to guarantee that 'connected' is setting to true. */
qmp("{ 'execute' : 'query-status'}");
ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf));
g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
close(send_sock[0]);
ret = qemu_recv(recv_sock, &len, sizeof(len), 0);
g_assert_cmpint(ret, ==, sizeof(len));
len = ntohl(len);
g_assert_cmpint(len, ==, sizeof(send_buf));
recv_buf = g_malloc(len);
ret = qemu_recv(recv_sock, recv_buf, len, 0);
g_assert_cmpstr(recv_buf, ==, send_buf);
g_free(recv_buf);
close(recv_sock);
unlink(sock_path);
#endif
}
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/netfilter/mirror", test_mirror);
ret = g_test_run();
qtest_end();
return ret;
}

View File

@ -0,0 +1,221 @@
/*
* QTest testcase for filter-redirector
*
* Copyright (c) 2016 FUJITSU LIMITED
* 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.
*
* Case 1, tx traffic flow:
*
* qemu side | test side
* |
* +---------+ | +-------+
* | backend <---------------+ sock0 |
* +----+----+ | +-------+
* | |
* +----v----+ +-------+ |
* | rd0 +->+chardev| |
* +---------+ +---+---+ |
* | |
* +---------+ | |
* | rd1 <------+ |
* +----+----+ |
* | |
* +----v----+ | +-------+
* | rd2 +--------------->sock1 |
* +---------+ | +-------+
* +
*
* --------------------------------------
* Case 2, rx traffic flow
* qemu side | test side
* |
* +---------+ | +-------+
* | backend +---------------> sock1 |
* +----^----+ | +-------+
* | |
* +----+----+ +-------+ |
* | rd0 +<-+chardev| |
* +---------+ +---+---+ |
* ^ |
* +---------+ | |
* | rd1 +------+ |
* +----^----+ |
* | |
* +----+----+ | +-------+
* | rd2 <---------------+sock0 |
* +---------+ | +-------+
* +
*/
#include "qemu/osdep.h"
#include <glib.h>
#include "libqtest.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
static void test_redirector_tx(void)
{
#ifndef _WIN32
/* socketpair(PF_UNIX) which does not exist on windows */
int backend_sock[2], recv_sock;
char *cmdline;
uint32_t ret = 0, len = 0;
char send_buf[] = "Hello!!";
char sock_path0[] = "filter-redirector0.XXXXXX";
char sock_path1[] = "filter-redirector1.XXXXXX";
char *recv_buf;
uint32_t size = sizeof(send_buf);
size = htonl(size);
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path0);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path1);
g_assert_cmpint(ret, !=, -1);
cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d "
"-device rtl8139,netdev=qtest-bn0,id=qtest-e0 "
"-chardev socket,id=redirector0,path=%s,server,nowait "
"-chardev socket,id=redirector1,path=%s,server,nowait "
"-chardev socket,id=redirector2,path=%s,nowait "
"-object filter-redirector,id=qtest-f0,netdev=qtest-bn0,"
"queue=tx,outdev=redirector0 "
"-object filter-redirector,id=qtest-f1,netdev=qtest-bn0,"
"queue=tx,indev=redirector2 "
"-object filter-redirector,id=qtest-f2,netdev=qtest-bn0,"
"queue=tx,outdev=redirector1 "
, backend_sock[1], sock_path0, sock_path1, sock_path0);
qtest_start(cmdline);
g_free(cmdline);
recv_sock = unix_connect(sock_path1, NULL);
g_assert_cmpint(recv_sock, !=, -1);
/* send a qmp command to guarantee that 'connected' is setting to true. */
qmp("{ 'execute' : 'query-status'}");
struct iovec iov[] = {
{
.iov_base = &size,
.iov_len = sizeof(size),
}, {
.iov_base = send_buf,
.iov_len = sizeof(send_buf),
},
};
ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf));
g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
close(backend_sock[0]);
ret = qemu_recv(recv_sock, &len, sizeof(len), 0);
g_assert_cmpint(ret, ==, sizeof(len));
len = ntohl(len);
g_assert_cmpint(len, ==, sizeof(send_buf));
recv_buf = g_malloc(len);
ret = qemu_recv(recv_sock, recv_buf, len, 0);
g_assert_cmpstr(recv_buf, ==, send_buf);
g_free(recv_buf);
close(recv_sock);
unlink(sock_path0);
unlink(sock_path1);
qtest_end();
#endif
}
static void test_redirector_rx(void)
{
#ifndef _WIN32
/* socketpair(PF_UNIX) which does not exist on windows */
int backend_sock[2], send_sock;
char *cmdline;
uint32_t ret = 0, len = 0;
char send_buf[] = "Hello!!";
char sock_path0[] = "filter-redirector0.XXXXXX";
char sock_path1[] = "filter-redirector1.XXXXXX";
char *recv_buf;
uint32_t size = sizeof(send_buf);
size = htonl(size);
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path0);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path1);
g_assert_cmpint(ret, !=, -1);
cmdline = g_strdup_printf("-netdev socket,id=qtest-bn0,fd=%d "
"-device rtl8139,netdev=qtest-bn0,id=qtest-e0 "
"-chardev socket,id=redirector0,path=%s,server,nowait "
"-chardev socket,id=redirector1,path=%s,server,nowait "
"-chardev socket,id=redirector2,path=%s,nowait "
"-object filter-redirector,id=qtest-f0,netdev=qtest-bn0,"
"queue=rx,indev=redirector0 "
"-object filter-redirector,id=qtest-f1,netdev=qtest-bn0,"
"queue=rx,outdev=redirector2 "
"-object filter-redirector,id=qtest-f2,netdev=qtest-bn0,"
"queue=rx,indev=redirector1 "
, backend_sock[1], sock_path0, sock_path1, sock_path0);
qtest_start(cmdline);
g_free(cmdline);
struct iovec iov[] = {
{
.iov_base = &size,
.iov_len = sizeof(size),
}, {
.iov_base = send_buf,
.iov_len = sizeof(send_buf),
},
};
send_sock = unix_connect(sock_path1, NULL);
g_assert_cmpint(send_sock, !=, -1);
/* send a qmp command to guarantee that 'connected' is setting to true. */
qmp("{ 'execute' : 'query-status'}");
ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf));
g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
close(send_sock);
ret = qemu_recv(backend_sock[0], &len, sizeof(len), 0);
g_assert_cmpint(ret, ==, sizeof(len));
len = ntohl(len);
g_assert_cmpint(len, ==, sizeof(send_buf));
recv_buf = g_malloc(len);
ret = qemu_recv(backend_sock[0], recv_buf, len, 0);
g_assert_cmpstr(recv_buf, ==, send_buf);
g_free(recv_buf);
unlink(sock_path0);
unlink(sock_path1);
qtest_end();
#endif
}
int main(int argc, char **argv)
{
int ret;
g_test_init(&argc, &argv, NULL);
qtest_add_func("/netfilter/redirector_tx", test_redirector_tx);
qtest_add_func("/netfilter/redirector_rx", test_redirector_rx);
ret = g_test_run();
return ret;
}

4
vl.c
View File

@ -2841,7 +2841,9 @@ static bool object_create_initial(const char *type)
* they depend on netdevs already existing
*/
if (g_str_equal(type, "filter-buffer") ||
g_str_equal(type, "filter-dump")) {
g_str_equal(type, "filter-dump") ||
g_str_equal(type, "filter-mirror") ||
g_str_equal(type, "filter-redirector")) {
return false;
}