mirror of https://github.com/xemu-project/xemu.git
-----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:
commit
8850dcbfd7
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -68,5 +68,7 @@ test-write-threshold
|
|||
test-x86-cpuid
|
||||
test-xbzrle
|
||||
test-netfilter
|
||||
test-filter-mirror
|
||||
test-filter-redirector
|
||||
*-test
|
||||
qapi-schema/*.test.*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
4
vl.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue