diff mbox

net/filter-redirector:Add filter-redirector

Message ID 1454655023-29701-1-git-send-email-zhangchen.fnst@cn.fujitsu.com (mailing list archive)
State New, archived
Headers show

Commit Message

Zhang Chen Feb. 5, 2016, 6:50 a.m. UTC
From: ZhangChen <zhangchen.fnst@cn.fujitsu.com>

Filter-redirector is a netfilter plugin.
It gives qemu the ability to redirect net packet.
redirector can redirect filter's net packet to outdev.
and redirect indev's packet to filter.

                             filter
                               +
                               |
                               |
              redirector       |
               +-------------------------+
               |               |         |
               |               |         |
               |               |         |
  indev +----------------+     +---------------->  outdev
               |         |               |
               |         |               |
               |         |               |
               +-------------------------+
                         |
                         |
                         v
                      filter

usage:

-netdev tap,id=hn0
-chardev socket,id=s0,host=ip_primary,port=X,server,nowait
-chardev socket,id=s1,host=ip_primary,port=Y,server,nowait
-filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0,outdev=s1

Signed-off-by: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
---
 net/Makefile.objs       |   1 +
 net/filter-redirector.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx         |   6 ++
 vl.c                    |   3 +-
 4 files changed, 254 insertions(+), 1 deletion(-)
 create mode 100644 net/filter-redirector.c

Comments

Jason Wang Feb. 18, 2016, 2:41 a.m. UTC | #1
On 02/05/2016 02:50 PM, Zhang Chen wrote:
> From: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>
> Filter-redirector is a netfilter plugin.
> It gives qemu the ability to redirect net packet.
> redirector can redirect filter's net packet to outdev.
> and redirect indev's packet to filter.
>
>                              filter
>                                +
>                                |
>                                |
>               redirector       |
>                +-------------------------+
>                |               |         |
>                |               |         |
>                |               |         |
>   indev +----------------+     +---------------->  outdev
>                |         |               |
>                |         |               |
>                |         |               |
>                +-------------------------+
>                          |
>                          |
>                          v
>                       filter
>
> usage:
>
> -netdev tap,id=hn0
> -chardev socket,id=s0,host=ip_primary,port=X,server,nowait
> -chardev socket,id=s1,host=ip_primary,port=Y,server,nowait
> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0,outdev=s1
>
> Signed-off-by: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
> ---

Thanks a lot for the patch. Like mirror, let's design a unit-test for
this. And what's more, is there any chance to unify the codes? (At least
parts of the codes could be reused).

>  net/Makefile.objs       |   1 +
>  net/filter-redirector.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++
>  qemu-options.hx         |   6 ++
>  vl.c                    |   3 +-
>  4 files changed, 254 insertions(+), 1 deletion(-)
>  create mode 100644 net/filter-redirector.c
>
> diff --git a/net/Makefile.objs b/net/Makefile.objs
> index 5fa2f97..f4290a5 100644
> --- a/net/Makefile.objs
> +++ b/net/Makefile.objs
> @@ -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-redirector.o
> diff --git a/net/filter-redirector.c b/net/filter-redirector.c
> new file mode 100644
> index 0000000..364e463
> --- /dev/null
> +++ b/net/filter-redirector.c
> @@ -0,0 +1,245 @@
> +/*
> + * 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 "net/filter.h"
> +#include "net/net.h"
> +#include "qemu-common.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_REDIRECTOR(obj) \
> +    OBJECT_CHECK(RedirectorState, (obj), TYPE_FILTER_REDIRECTOR)
> +
> +#define TYPE_FILTER_REDIRECTOR "filter-redirector"
> +#define REDIRECT_HEADER_LEN sizeof(uint32_t)
> +
> +typedef struct RedirectorState {
> +    NetFilterState parent_obj;
> +    NetQueue *incoming_queue;/* guest normal net queue */

The comment looks unless and maybe even wrong when queue=rx?

> +    char *indev;
> +    char *outdev;
> +    CharDriverState *chr_in;
> +    CharDriverState *chr_out;
> +} RedirectorState;
> +
> +static ssize_t filter_redirector_send(NetFilterState *nf,
> +                                   const struct iovec *iov,
> +                                   int iovcnt)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
> +    ssize_t ret = 0;
> +    ssize_t size = 0;
> +    uint32_t len =  0;
> +    char *buf;
> +
> +    size = iov_size(iov, iovcnt);
> +    len = htonl(size);
> +    if (!size) {
> +        return 0;
> +    }
> +
> +    buf = g_malloc0(size);
> +    iov_to_buf(iov, iovcnt, 0, buf, size);
> +    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)&len, sizeof(len));
> +    if (ret < 0) {

Similar to mirror, need check the return value against sizeof(len). And
the code could be simplified with something like:

goto out;

...

out:
    g_free(buf);
    return ret;

And you can see another advantages of unifying the codes here, avoid
duplicating bugx/fixes.

> +        g_free(buf);
> +        return ret;
> +    }
> +
> +    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)buf, size);
> +    g_free(buf);
> +    return ret;
> +}
> +
> +static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
> +                                         NetClientState *sender,
> +                                         unsigned flags,
> +                                         const struct iovec *iov,
> +                                         int iovcnt,
> +                                         NetPacketSent *sent_cb)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
> +    ssize_t ret = 0;
> +
> +    if (s->chr_out) {
> +        ret = filter_redirector_send(nf, iov, iovcnt);
> +        if (ret < 0) {
> +            error_report("filter_redirector_send failed");
> +        }
> +    }
> +    return iov_size(iov, iovcnt);
> +}
> +
> +static void filter_redirector_cleanup(NetFilterState *nf)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
> +
> +    if (s->chr_in) {
> +        qemu_chr_fe_release(s->chr_in);

release but no claim?

> +    }
> +    if (s->chr_out) {
> +        qemu_chr_fe_release(s->chr_out);
> +    }
> +}
> +
> +static int redirector_chr_can_read(void *opaque)
> +{
> +    return REDIRECT_HEADER_LEN;
> +}
> +
> +static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
> +{
> +    NetFilterState *nf = opaque;
> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
> +    uint32_t len;
> +    ssize_t ret = 0;
> +    uint8_t *recv_buf;
> +
> +    if (size != REDIRECT_HEADER_LEN) {
> +        // FIXME: do something else

This is not packet corruption since I believe you want tcp socket here
which is doing byte stream. Need some logic to prepare to wait for all
the bytes for length is received.

Though sub-optimal (since it uses usleep() which may block iothread),
maybe you could just do a for simplicity:

qemu_chr_fe_read_all(s->chr_in, recv_buf, sizeof(len));

If you wish, you can have a look at socket backend (net_socket_send) who
has a better solution for this.
> +        error_report("packet is corruption, drop it");
> +        return;
> +    }
> +    memcpy(&len, buf, sizeof(len));
> +    len = ntohl(len);
> +
> +    if (len > 0 && len < NET_BUFSIZE) {
> +        recv_buf = g_malloc0(len);

Looks like g_malloc() is sufficient here.

> +        ret = qemu_chr_fe_read_all(s->chr_in, recv_buf, len);
> +        if (ret != len) {
> +            error_report("filter-redirector recv buf failed");
> +            g_free(recv_buf);
> +            return;
> +        }
> +
> +        ret = qemu_net_queue_send(s->incoming_queue, nf->netdev,
> +                            0, (const uint8_t *)recv_buf, len, NULL);
> +        g_free(recv_buf);
> +        if (ret < 0) {
> +            error_report("filter-redirector out to guest failed");

"guest" is inaccurate here, what if queue=rx?

> +        }
> +    } else {
> +        error_report("filter-redirector recv len failed");
> +    }
> +}
> +
> +static void filter_redirector_setup(NetFilterState *nf, Error **errp)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
> +
> +    if (!s->indev && !s->outdev) {
> +        error_setg(errp, "filter redirector needs 'indev' or "
> +                "'outdev' at least one property set");
> +        return;
> +    }
> +
> +    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_add_handlers(s->chr_in, redirector_chr_can_read,
> +                              redirector_chr_read, NULL, 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;
> +        }
> +    }
> +    s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
> +}
> +
> +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)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
> +
> +    return g_strdup(s->indev);
> +}
> +
> +static void
> +filter_redirector_set_indev(Object *obj, const char *value, Error **errp)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
> +
> +    g_free(s->indev);
> +    s->indev = g_strdup(value);
> +}
> +
> +static char *filter_redirector_get_outdev(Object *obj, Error **errp)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
> +
> +    return g_strdup(s->outdev);
> +}
> +
> +static void
> +filter_redirector_set_outdev(Object *obj, const char *value, Error **errp)
> +{
> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
> +
> +    g_free(s->outdev);
> +    s->outdev = g_strdup(value);
> +}
> +
> +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_redirector_fini(Object *obj)
> +{
> +    RedirectorState *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(RedirectorState),
> +};
> +
> +static void register_types(void)
> +{
> +    type_register_static(&filter_redirector_info);
> +}
> +
> +type_init(register_types);
> diff --git a/qemu-options.hx b/qemu-options.hx
> index f31a240..1f796ed 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -3745,6 +3745,12 @@ 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-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
> +
> +filter-redirector on netdev @var{netdevid},redirector can redirect filter's net packet to outdev.
> +and redirect indev's packet to filter.

Exceeds 80 characters limit. And need more work. e.g:

- Need to differs outdev id from indev id.
- Need to clarify at least one of outdev and indev needs to be set
- We're in fact redirect packet between chardev and netdev's queue (not
filter).

> +queue @var{all|rx|tx} is an option that can be applied to filter-redirector.

This line is unnecessary, we've already had

"
queue @var{all|rx|tx} is an option that can be applied to any netfilter.
"

> +
>  @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
> diff --git a/vl.c b/vl.c
> index f043009..4a9a9a0 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -2801,7 +2801,8 @@ 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-redirector")) {
>          return false;
>      }
>
Zhang Chen Feb. 18, 2016, 7:50 a.m. UTC | #2
On 02/18/2016 10:41 AM, Jason Wang wrote:
>
> On 02/05/2016 02:50 PM, Zhang Chen wrote:
>> From: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>>
>> Filter-redirector is a netfilter plugin.
>> It gives qemu the ability to redirect net packet.
>> redirector can redirect filter's net packet to outdev.
>> and redirect indev's packet to filter.
>>
>>                               filter
>>                                 +
>>                                 |
>>                                 |
>>                redirector       |
>>                 +-------------------------+
>>                 |               |         |
>>                 |               |         |
>>                 |               |         |
>>    indev +----------------+     +---------------->  outdev
>>                 |         |               |
>>                 |         |               |
>>                 |         |               |
>>                 +-------------------------+
>>                           |
>>                           |
>>                           v
>>                        filter

                                   v

change it to           filter ........ filter ...... guest
It's may more clearly expressed.

>> usage:
>>
>> -netdev tap,id=hn0
>> -chardev socket,id=s0,host=ip_primary,port=X,server,nowait
>> -chardev socket,id=s1,host=ip_primary,port=Y,server,nowait
>> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0,outdev=s1
>>
>> Signed-off-by: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
>> ---
> Thanks a lot for the patch. Like mirror, let's design a unit-test for
> this. And what's more, is there any chance to unify the codes? (At least
> parts of the codes could be reused).

We can make filter-redirector based on filter-mirror.
if you want to use redirector ,you must open mirror before.
like this:

-netdev tap,id=hn0
-chardev socket,id=mirror0,host=ip_primary,port=X,server,nowait
-filter-mirror,id=m0,netdev=hn0,queue=tx/rx/all,redirector=on,outdev=mirror0
-filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0

How about this?


>>   net/Makefile.objs       |   1 +
>>   net/filter-redirector.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++++
>>   qemu-options.hx         |   6 ++
>>   vl.c                    |   3 +-
>>   4 files changed, 254 insertions(+), 1 deletion(-)
>>   create mode 100644 net/filter-redirector.c
>>
>> diff --git a/net/Makefile.objs b/net/Makefile.objs
>> index 5fa2f97..f4290a5 100644
>> --- a/net/Makefile.objs
>> +++ b/net/Makefile.objs
>> @@ -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-redirector.o
>> diff --git a/net/filter-redirector.c b/net/filter-redirector.c
>> new file mode 100644
>> index 0000000..364e463
>> --- /dev/null
>> +++ b/net/filter-redirector.c
>> @@ -0,0 +1,245 @@
>> +/*
>> + * 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 "net/filter.h"
>> +#include "net/net.h"
>> +#include "qemu-common.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_REDIRECTOR(obj) \
>> +    OBJECT_CHECK(RedirectorState, (obj), TYPE_FILTER_REDIRECTOR)
>> +
>> +#define TYPE_FILTER_REDIRECTOR "filter-redirector"
>> +#define REDIRECT_HEADER_LEN sizeof(uint32_t)
>> +
>> +typedef struct RedirectorState {
>> +    NetFilterState parent_obj;
>> +    NetQueue *incoming_queue;/* guest normal net queue */
> The comment looks unless and maybe even wrong when queue=rx?

We design redirector that indev's data always be passed to guest finally.
so, It's no relation between the queue=rx/tx/all. just related to indev 
= xxx.
we need incoming_queue to inject packet from indev.


>> +    char *indev;
>> +    char *outdev;
>> +    CharDriverState *chr_in;
>> +    CharDriverState *chr_out;
>> +} RedirectorState;
>> +
>> +static ssize_t filter_redirector_send(NetFilterState *nf,
>> +                                   const struct iovec *iov,
>> +                                   int iovcnt)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
>> +    ssize_t ret = 0;
>> +    ssize_t size = 0;
>> +    uint32_t len =  0;
>> +    char *buf;
>> +
>> +    size = iov_size(iov, iovcnt);
>> +    len = htonl(size);
>> +    if (!size) {
>> +        return 0;
>> +    }
>> +
>> +    buf = g_malloc0(size);
>> +    iov_to_buf(iov, iovcnt, 0, buf, size);
>> +    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)&len, sizeof(len));
>> +    if (ret < 0) {
> Similar to mirror, need check the return value against sizeof(len). And
> the code could be simplified with something like:
>
> goto out;
>
> ...
>
> out:
>      g_free(buf);
>      return ret;
>
> And you can see another advantages of unifying the codes here, avoid
> duplicating bugx/fixes.

Thanks , I will fix it in next version.

>> +        g_free(buf);
>> +        return ret;
>> +    }
>> +
>> +    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)buf, size);
>> +    g_free(buf);
>> +    return ret;
>> +}
>> +
>> +static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
>> +                                         NetClientState *sender,
>> +                                         unsigned flags,
>> +                                         const struct iovec *iov,
>> +                                         int iovcnt,
>> +                                         NetPacketSent *sent_cb)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
>> +    ssize_t ret = 0;
>> +
>> +    if (s->chr_out) {
>> +        ret = filter_redirector_send(nf, iov, iovcnt);
>> +        if (ret < 0) {
>> +            error_report("filter_redirector_send failed");
>> +        }
>> +    }
>> +    return iov_size(iov, iovcnt);
>> +}
>> +
>> +static void filter_redirector_cleanup(NetFilterState *nf)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
>> +
>> +    if (s->chr_in) {
>> +        qemu_chr_fe_release(s->chr_in);
> release but no claim?

will fix it in next version.

>> +    }
>> +    if (s->chr_out) {
>> +        qemu_chr_fe_release(s->chr_out);
>> +    }
>> +}
>> +
>> +static int redirector_chr_can_read(void *opaque)
>> +{
>> +    return REDIRECT_HEADER_LEN;
>> +}
>> +
>> +static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
>> +{
>> +    NetFilterState *nf = opaque;
>> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
>> +    uint32_t len;
>> +    ssize_t ret = 0;
>> +    uint8_t *recv_buf;
>> +
>> +    if (size != REDIRECT_HEADER_LEN) {
>> +        // FIXME: do something else
> This is not packet corruption since I believe you want tcp socket here
> which is doing byte stream. Need some logic to prepare to wait for all
> the bytes for length is received.
>
> Though sub-optimal (since it uses usleep() which may block iothread),
> maybe you could just do a for simplicity:
>
> qemu_chr_fe_read_all(s->chr_in, recv_buf, sizeof(len));
>
> If you wish, you can have a look at socket backend (net_socket_send) who
> has a better solution for this.

Get it~
I will fix it in next version.


>> +        error_report("packet is corruption, drop it");
>> +        return;
>> +    }
>> +    memcpy(&len, buf, sizeof(len));
>> +    len = ntohl(len);
>> +
>> +    if (len > 0 && len < NET_BUFSIZE) {
>> +        recv_buf = g_malloc0(len);
> Looks like g_malloc() is sufficient here.

OK, will fix it in next version.

>
>> +        ret = qemu_chr_fe_read_all(s->chr_in, recv_buf, len);
>> +        if (ret != len) {
>> +            error_report("filter-redirector recv buf failed");
>> +            g_free(recv_buf);
>> +            return;
>> +        }
>> +
>> +        ret = qemu_net_queue_send(s->incoming_queue, nf->netdev,
>> +                            0, (const uint8_t *)recv_buf, len, NULL);
>> +        g_free(recv_buf);
>> +        if (ret < 0) {
>> +            error_report("filter-redirector out to guest failed");
> "guest" is inaccurate here, what if queue=rx?

indev is not influenced by queue=rx/tx/all, so it always redirect to guest.

>
>> +        }
>> +    } else {
>> +        error_report("filter-redirector recv len failed");
>> +    }
>> +}
>> +
>> +static void filter_redirector_setup(NetFilterState *nf, Error **errp)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(nf);
>> +
>> +    if (!s->indev && !s->outdev) {
>> +        error_setg(errp, "filter redirector needs 'indev' or "
>> +                "'outdev' at least one property set");
>> +        return;
>> +    }
>> +
>> +    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_add_handlers(s->chr_in, redirector_chr_can_read,
>> +                              redirector_chr_read, NULL, 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;
>> +        }
>> +    }
>> +    s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
>> +}
>> +
>> +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)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
>> +
>> +    return g_strdup(s->indev);
>> +}
>> +
>> +static void
>> +filter_redirector_set_indev(Object *obj, const char *value, Error **errp)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
>> +
>> +    g_free(s->indev);
>> +    s->indev = g_strdup(value);
>> +}
>> +
>> +static char *filter_redirector_get_outdev(Object *obj, Error **errp)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
>> +
>> +    return g_strdup(s->outdev);
>> +}
>> +
>> +static void
>> +filter_redirector_set_outdev(Object *obj, const char *value, Error **errp)
>> +{
>> +    RedirectorState *s = FILTER_REDIRECTOR(obj);
>> +
>> +    g_free(s->outdev);
>> +    s->outdev = g_strdup(value);
>> +}
>> +
>> +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_redirector_fini(Object *obj)
>> +{
>> +    RedirectorState *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(RedirectorState),
>> +};
>> +
>> +static void register_types(void)
>> +{
>> +    type_register_static(&filter_redirector_info);
>> +}
>> +
>> +type_init(register_types);
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index f31a240..1f796ed 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -3745,6 +3745,12 @@ 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-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
>> +
>> +filter-redirector on netdev @var{netdevid},redirector can redirect filter's net packet to outdev.
>> +and redirect indev's packet to filter.
> Exceeds 80 characters limit. And need more work. e.g:
>
> - Need to differs outdev id from indev id.
> - Need to clarify at least one of outdev and indev needs to be set
> - We're in fact redirect packet between chardev and netdev's queue (not
> filter).

OK, I will add that comments in next version.

>> +queue @var{all|rx|tx} is an option that can be applied to filter-redirector.
> This line is unnecessary, we've already had
>
> "
> queue @var{all|rx|tx} is an option that can be applied to any netfilter.
> "

OK, I will remove it.

Thanks
zhangchen

>> +
>>   @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
>> diff --git a/vl.c b/vl.c
>> index f043009..4a9a9a0 100644
>> --- a/vl.c
>> +++ b/vl.c
>> @@ -2801,7 +2801,8 @@ 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-redirector")) {
>>           return false;
>>       }
>>   
>
>
> .
>
Jason Wang Feb. 24, 2016, 3:39 a.m. UTC | #3
On 02/18/2016 03:50 PM, Zhang Chen wrote:
>
>
> On 02/18/2016 10:41 AM, Jason Wang wrote:
>>
>> On 02/05/2016 02:50 PM, Zhang Chen wrote:
>>> From: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>>>
>>> Filter-redirector is a netfilter plugin.
>>> It gives qemu the ability to redirect net packet.
>>> redirector can redirect filter's net packet to outdev.
>>> and redirect indev's packet to filter.
>>>
>>>                               filter
>>>                                 +
>>>                                 |
>>>                                 |
>>>                redirector       |
>>>                 +-------------------------+
>>>                 |               |         |
>>>                 |               |         |
>>>                 |               |         |
>>>    indev +----------------+     +---------------->  outdev
>>>                 |         |               |
>>>                 |         |               |
>>>                 |         |               |
>>>                 +-------------------------+
>>>                           |
>>>                           |
>>>                           v
>>>                        filter
>
>                                   v
>
> change it to           filter ........ filter ...... guest
> It's may more clearly expressed.
>
>>> usage:
>>>
>>> -netdev tap,id=hn0
>>> -chardev socket,id=s0,host=ip_primary,port=X,server,nowait
>>> -chardev socket,id=s1,host=ip_primary,port=Y,server,nowait
>>> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0,outdev=s1
>>>
>>> Signed-off-by: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>>> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
>>> ---
>> Thanks a lot for the patch. Like mirror, let's design a unit-test for
>> this. And what's more, is there any chance to unify the codes? (At least
>> parts of the codes could be reused).
>
> We can make filter-redirector based on filter-mirror.
> if you want to use redirector ,you must open mirror before.
> like this:
>
> -netdev tap,id=hn0
> -chardev socket,id=mirror0,host=ip_primary,port=X,server,nowait
> -filter-mirror,id=m0,netdev=hn0,queue=tx/rx/all,redirector=on,outdev=mirror0
>
> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0
>
> How about this?

This looks like a burden for user who just want to use redirector. Maybe
we can do :

- Still two type of filters but sharing a single state.
- Using a internal flag to differ mirrors from redirectors?

>
>
>>>   net/Makefile.objs       |   1 +
>>>   net/filter-redirector.c | 245
>>> ++++++++++++++++++++++++++++++++++++++++++++++++
>>>   qemu-options.hx         |   6 ++
>>>   vl.c                    |   3 +-
>>>   4 files changed, 254 insertions(+), 1 deletion(-)
>>>   create mode 100644 net/filter-redirector.c
>>>
>>> diff --git a/net/Makefile.objs b/net/Makefile.objs
>>> index 5fa2f97..f4290a5 100644
>>> --- a/net/Makefile.objs
>>> +++ b/net/Makefile.objs
>>> @@ -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-redirector.o
>>> diff --git a/net/filter-redirector.c b/net/filter-redirector.c
>>> new file mode 100644
>>> index 0000000..364e463
>>> --- /dev/null
>>> +++ b/net/filter-redirector.c
>>> @@ -0,0 +1,245 @@
>>> +/*
>>> + * 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 "net/filter.h"
>>> +#include "net/net.h"
>>> +#include "qemu-common.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_REDIRECTOR(obj) \
>>> +    OBJECT_CHECK(RedirectorState, (obj), TYPE_FILTER_REDIRECTOR)
>>> +
>>> +#define TYPE_FILTER_REDIRECTOR "filter-redirector"
>>> +#define REDIRECT_HEADER_LEN sizeof(uint32_t)
>>> +
>>> +typedef struct RedirectorState {
>>> +    NetFilterState parent_obj;
>>> +    NetQueue *incoming_queue;/* guest normal net queue */
>> The comment looks unless and maybe even wrong when queue=rx?
>
> We design redirector that indev's data always be passed to guest finally.
> so, It's no relation between the queue=rx/tx/all. just related to
> indev = xxx.
> we need incoming_queue to inject packet from indev.

So what happens if queue=rx or you want to forbid queue=rx for redirector?
Zhang Chen Feb. 24, 2016, 9:03 a.m. UTC | #4
On 02/24/2016 11:39 AM, Jason Wang wrote:
>
> On 02/18/2016 03:50 PM, Zhang Chen wrote:
>>
>> On 02/18/2016 10:41 AM, Jason Wang wrote:
>>> On 02/05/2016 02:50 PM, Zhang Chen wrote:
>>>> From: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>>>>
>>>> Filter-redirector is a netfilter plugin.
>>>> It gives qemu the ability to redirect net packet.
>>>> redirector can redirect filter's net packet to outdev.
>>>> and redirect indev's packet to filter.
>>>>
>>>>                                filter
>>>>                                  +
>>>>                                  |
>>>>                                  |
>>>>                 redirector       |
>>>>                  +-------------------------+
>>>>                  |               |         |
>>>>                  |               |         |
>>>>                  |               |         |
>>>>     indev +----------------+     +---------------->  outdev
>>>>                  |         |               |
>>>>                  |         |               |
>>>>                  |         |               |
>>>>                  +-------------------------+
>>>>                            |
>>>>                            |
>>>>                            v
>>>>                         filter
>>                                    v
>>
>> change it to           filter ........ filter ...... guest
>> It's may more clearly expressed.
>>
>>>> usage:
>>>>
>>>> -netdev tap,id=hn0
>>>> -chardev socket,id=s0,host=ip_primary,port=X,server,nowait
>>>> -chardev socket,id=s1,host=ip_primary,port=Y,server,nowait
>>>> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0,outdev=s1
>>>>
>>>> Signed-off-by: ZhangChen <zhangchen.fnst@cn.fujitsu.com>
>>>> Signed-off-by: Wen Congyang <wency@cn.fujitsu.com>
>>>> ---
>>> Thanks a lot for the patch. Like mirror, let's design a unit-test for
>>> this. And what's more, is there any chance to unify the codes? (At least
>>> parts of the codes could be reused).
>> We can make filter-redirector based on filter-mirror.
>> if you want to use redirector ,you must open mirror before.
>> like this:
>>
>> -netdev tap,id=hn0
>> -chardev socket,id=mirror0,host=ip_primary,port=X,server,nowait
>> -filter-mirror,id=m0,netdev=hn0,queue=tx/rx/all,redirector=on,outdev=mirror0
>>
>> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,indev=s0
>>
>> How about this?
> This looks like a burden for user who just want to use redirector. Maybe
> we can do :
>
> - Still two type of filters but sharing a single state.
> - Using a internal flag to differ mirrors from redirectors?

Good idea~ I will change it in next version.

>
>>
>>>>    net/Makefile.objs       |   1 +
>>>>    net/filter-redirector.c | 245
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++
>>>>    qemu-options.hx         |   6 ++
>>>>    vl.c                    |   3 +-
>>>>    4 files changed, 254 insertions(+), 1 deletion(-)
>>>>    create mode 100644 net/filter-redirector.c
>>>>
>>>> diff --git a/net/Makefile.objs b/net/Makefile.objs
>>>> index 5fa2f97..f4290a5 100644
>>>> --- a/net/Makefile.objs
>>>> +++ b/net/Makefile.objs
>>>> @@ -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-redirector.o
>>>> diff --git a/net/filter-redirector.c b/net/filter-redirector.c
>>>> new file mode 100644
>>>> index 0000000..364e463
>>>> --- /dev/null
>>>> +++ b/net/filter-redirector.c
>>>> @@ -0,0 +1,245 @@
>>>> +/*
>>>> + * 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 "net/filter.h"
>>>> +#include "net/net.h"
>>>> +#include "qemu-common.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_REDIRECTOR(obj) \
>>>> +    OBJECT_CHECK(RedirectorState, (obj), TYPE_FILTER_REDIRECTOR)
>>>> +
>>>> +#define TYPE_FILTER_REDIRECTOR "filter-redirector"
>>>> +#define REDIRECT_HEADER_LEN sizeof(uint32_t)
>>>> +
>>>> +typedef struct RedirectorState {
>>>> +    NetFilterState parent_obj;
>>>> +    NetQueue *incoming_queue;/* guest normal net queue */
>>> The comment looks unless and maybe even wrong when queue=rx?
>> We design redirector that indev's data always be passed to guest finally.
>> so, It's no relation between the queue=rx/tx/all. just related to
>> indev = xxx.
>> we need incoming_queue to inject packet from indev.
> So what happens if queue=rx or you want to forbid queue=rx for redirector?
>

If queue=rx, filter-redirector will get the packet that guest send, then 
redirect
to outdev(if none, do nothing). but queue=rx/tx/all not related to 
indev. please
look the flow chart below. queue=xxx just work for one way(filter->outdev).

                               filter
                                 +
                                 |
                                 |
                redirector       |
                 +-------------------------+
                 |               |         |
                 |               |         |
                 |               |         |
    indev +----------------+     +---------------->  outdev
                 |         |               |
                 |         |               |
                 |         |               |
                 +-------------------------+
                           |
                           |
                           v
                        filter

                           |

                           |

                           v
                        filter ........ filter ...... guest

>
> .
>
Jason Wang Feb. 29, 2016, 7:11 a.m. UTC | #5
On 02/24/2016 05:03 PM, Zhang Chen wrote:
>
> If queue=rx, filter-redirector will get the packet that guest send,
> then redirect
> to outdev(if none, do nothing). but queue=rx/tx/all not related to
> indev. please
> look the flow chart below. queue=xxx just work for one
> way(filter->outdev).
>
>                               filter
>                                 +
>                                 |
>                                 |
>                redirector       |
>                 +-------------------------+
>                 |               |         |
>                 |               |         |
>                 |               |         |
>    indev +----------------+     +---------------->  outdev
>                 |         |               |
>                 |         |               |
>                 |         |               |
>                 +-------------------------+
>                           |
>                           |
>                           v
>                        filter
>
>                           |
>
>                           |
>
>                           v
>                        filter ........ filter ...... guest
>

This looks a violation on the assumption of current filter behavior. 
Each filter should only talk to the 'next' or 'prev' filter on the chain
(depends on the direction) or netdev when queue=rx or netdev's peer when
queue=tx.

And in fact there's subtle differences with your patch:

When queue='all' since you force nf->netdev as sender, direction is
NET_FILTER_DIRECTION_TX, the packet will be passed to 'next' filter on
the chain.
When queue='rx', direction is NET_FILTER_DIRECTION_RX, the packet will
be pass to 'prev' filter on the chain.

So as you can see, 'all' is ambiguous here. I think we should keep
current behavior by redirecting traffic to netdev when queue='rx'. For
queue='all', maybe we need redirect the traffic to both netdev and
netdev's peer.
Zhang Chen Feb. 29, 2016, 12:33 p.m. UTC | #6
On 02/29/2016 03:11 PM, Jason Wang wrote:
>
> On 02/24/2016 05:03 PM, Zhang Chen wrote:
>> If queue=rx, filter-redirector will get the packet that guest send,
>> then redirect
>> to outdev(if none, do nothing). but queue=rx/tx/all not related to
>> indev. please
>> look the flow chart below. queue=xxx just work for one
>> way(filter->outdev).
>>
>>                                filter
>>                                  +
>>                                  |
>>                                  |
>>                 redirector       |
>>                  +-------------------------+
>>                  |               |         |
>>                  |               |         |
>>                  |               |         |
>>     indev +----------------+     +---------------->  outdev
>>                  |         |               |
>>                  |         |               |
>>                  |         |               |
>>                  +-------------------------+
>>                            |
>>                            |
>>                            v
>>                         filter
>>
>>                            |
>>
>>                            |
>>
>>                            v
>>                         filter ........ filter ...... guest
>>
> This looks a violation on the assumption of current filter behavior.
> Each filter should only talk to the 'next' or 'prev' filter on the chain
> (depends on the direction) or netdev when queue=rx or netdev's peer when
> queue=tx.
>
> And in fact there's subtle differences with your patch:
>
> When queue='all' since you force nf->netdev as sender, direction is
> NET_FILTER_DIRECTION_TX, the packet will be passed to 'next' filter on
> the chain.
> When queue='rx', direction is NET_FILTER_DIRECTION_RX, the packet will
> be pass to 'prev' filter on the chain.
>
> So as you can see, 'all' is ambiguous here. I think we should keep
> current behavior by redirecting traffic to netdev when queue='rx'. For
> queue='all', maybe we need redirect the traffic to both netdev and
> netdev's peer.
>
>

OK, I will change usage to :

-filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,outdev=s1,indev=s0,in_direction=tx/rx
How about this?

I will fix it in V3.

Thanks
zhangchen

>
>
> .
>
Jason Wang March 2, 2016, 5:41 a.m. UTC | #7
On 02/29/2016 08:33 PM, Zhang Chen wrote:
>
>
> On 02/29/2016 03:11 PM, Jason Wang wrote:
>>
>> On 02/24/2016 05:03 PM, Zhang Chen wrote:
>>> If queue=rx, filter-redirector will get the packet that guest send,
>>> then redirect
>>> to outdev(if none, do nothing). but queue=rx/tx/all not related to
>>> indev. please
>>> look the flow chart below. queue=xxx just work for one
>>> way(filter->outdev).
>>>
>>>                                filter
>>>                                  +
>>>                                  |
>>>                                  |
>>>                 redirector       |
>>>                  +-------------------------+
>>>                  |               |         |
>>>                  |               |         |
>>>                  |               |         |
>>>     indev +----------------+     +---------------->  outdev
>>>                  |         |               |
>>>                  |         |               |
>>>                  |         |               |
>>>                  +-------------------------+
>>>                            |
>>>                            |
>>>                            v
>>>                         filter
>>>
>>>                            |
>>>
>>>                            |
>>>
>>>                            v
>>>                         filter ........ filter ...... guest
>>>
>> This looks a violation on the assumption of current filter behavior.
>> Each filter should only talk to the 'next' or 'prev' filter on the chain
>> (depends on the direction) or netdev when queue=rx or netdev's peer when
>> queue=tx.
>>
>> And in fact there's subtle differences with your patch:
>>
>> When queue='all' since you force nf->netdev as sender, direction is
>> NET_FILTER_DIRECTION_TX, the packet will be passed to 'next' filter on
>> the chain.
>> When queue='rx', direction is NET_FILTER_DIRECTION_RX, the packet will
>> be pass to 'prev' filter on the chain.
>>
>> So as you can see, 'all' is ambiguous here. I think we should keep
>> current behavior by redirecting traffic to netdev when queue='rx'. For
>> queue='all', maybe we need redirect the traffic to both netdev and
>> netdev's peer.
>>
>>
>
> OK, I will change usage to :
>
> -filter-redirector,id=r0,netdev=hn0,queue=tx/rx/all,outdev=s1,indev=s0,in_direction=tx/rx
>
> How about this?

Looks like in_direction complicates the issue and is unnecessary. In
fact, it could be achieved by having another redirector.

>
> I will fix it in V3.
>
> Thanks
> zhangchen
diff mbox

Patch

diff --git a/net/Makefile.objs b/net/Makefile.objs
index 5fa2f97..f4290a5 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -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-redirector.o
diff --git a/net/filter-redirector.c b/net/filter-redirector.c
new file mode 100644
index 0000000..364e463
--- /dev/null
+++ b/net/filter-redirector.c
@@ -0,0 +1,245 @@ 
+/*
+ * 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 "net/filter.h"
+#include "net/net.h"
+#include "qemu-common.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_REDIRECTOR(obj) \
+    OBJECT_CHECK(RedirectorState, (obj), TYPE_FILTER_REDIRECTOR)
+
+#define TYPE_FILTER_REDIRECTOR "filter-redirector"
+#define REDIRECT_HEADER_LEN sizeof(uint32_t)
+
+typedef struct RedirectorState {
+    NetFilterState parent_obj;
+    NetQueue *incoming_queue;/* guest normal net queue */
+    char *indev;
+    char *outdev;
+    CharDriverState *chr_in;
+    CharDriverState *chr_out;
+} RedirectorState;
+
+static ssize_t filter_redirector_send(NetFilterState *nf,
+                                   const struct iovec *iov,
+                                   int iovcnt)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(nf);
+    ssize_t ret = 0;
+    ssize_t size = 0;
+    uint32_t len =  0;
+    char *buf;
+
+    size = iov_size(iov, iovcnt);
+    len = htonl(size);
+    if (!size) {
+        return 0;
+    }
+
+    buf = g_malloc0(size);
+    iov_to_buf(iov, iovcnt, 0, buf, size);
+    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)&len, sizeof(len));
+    if (ret < 0) {
+        g_free(buf);
+        return ret;
+    }
+
+    ret = qemu_chr_fe_write_all(s->chr_out, (uint8_t *)buf, size);
+    g_free(buf);
+    return ret;
+}
+
+static ssize_t filter_redirector_receive_iov(NetFilterState *nf,
+                                         NetClientState *sender,
+                                         unsigned flags,
+                                         const struct iovec *iov,
+                                         int iovcnt,
+                                         NetPacketSent *sent_cb)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(nf);
+    ssize_t ret = 0;
+
+    if (s->chr_out) {
+        ret = filter_redirector_send(nf, iov, iovcnt);
+        if (ret < 0) {
+            error_report("filter_redirector_send failed");
+        }
+    }
+    return iov_size(iov, iovcnt);
+}
+
+static void filter_redirector_cleanup(NetFilterState *nf)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(nf);
+
+    if (s->chr_in) {
+        qemu_chr_fe_release(s->chr_in);
+    }
+    if (s->chr_out) {
+        qemu_chr_fe_release(s->chr_out);
+    }
+}
+
+static int redirector_chr_can_read(void *opaque)
+{
+    return REDIRECT_HEADER_LEN;
+}
+
+static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+    NetFilterState *nf = opaque;
+    RedirectorState *s = FILTER_REDIRECTOR(nf);
+    uint32_t len;
+    ssize_t ret = 0;
+    uint8_t *recv_buf;
+
+    if (size != REDIRECT_HEADER_LEN) {
+        // FIXME: do something else
+        error_report("packet is corruption, drop it");
+        return;
+    }
+    memcpy(&len, buf, sizeof(len));
+    len = ntohl(len);
+
+    if (len > 0 && len < NET_BUFSIZE) {
+        recv_buf = g_malloc0(len);
+        ret = qemu_chr_fe_read_all(s->chr_in, recv_buf, len);
+        if (ret != len) {
+            error_report("filter-redirector recv buf failed");
+            g_free(recv_buf);
+            return;
+        }
+
+        ret = qemu_net_queue_send(s->incoming_queue, nf->netdev,
+                            0, (const uint8_t *)recv_buf, len, NULL);
+        g_free(recv_buf);
+        if (ret < 0) {
+            error_report("filter-redirector out to guest failed");
+        }
+    } else {
+        error_report("filter-redirector recv len failed");
+    }
+}
+
+static void filter_redirector_setup(NetFilterState *nf, Error **errp)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(nf);
+
+    if (!s->indev && !s->outdev) {
+        error_setg(errp, "filter redirector needs 'indev' or "
+                "'outdev' at least one property set");
+        return;
+    }
+
+    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_add_handlers(s->chr_in, redirector_chr_can_read,
+                              redirector_chr_read, NULL, 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;
+        }
+    }
+    s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
+}
+
+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)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(obj);
+
+    return g_strdup(s->indev);
+}
+
+static void
+filter_redirector_set_indev(Object *obj, const char *value, Error **errp)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(obj);
+
+    g_free(s->indev);
+    s->indev = g_strdup(value);
+}
+
+static char *filter_redirector_get_outdev(Object *obj, Error **errp)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(obj);
+
+    return g_strdup(s->outdev);
+}
+
+static void
+filter_redirector_set_outdev(Object *obj, const char *value, Error **errp)
+{
+    RedirectorState *s = FILTER_REDIRECTOR(obj);
+
+    g_free(s->outdev);
+    s->outdev = g_strdup(value);
+}
+
+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_redirector_fini(Object *obj)
+{
+    RedirectorState *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(RedirectorState),
+};
+
+static void register_types(void)
+{
+    type_register_static(&filter_redirector_info);
+}
+
+type_init(register_types);
diff --git a/qemu-options.hx b/qemu-options.hx
index f31a240..1f796ed 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3745,6 +3745,12 @@  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-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
+
+filter-redirector on netdev @var{netdevid},redirector can redirect filter's net packet to outdev.
+and redirect indev's packet to filter.
+queue @var{all|rx|tx} is an option that can be applied to filter-redirector.
+
 @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
diff --git a/vl.c b/vl.c
index f043009..4a9a9a0 100644
--- a/vl.c
+++ b/vl.c
@@ -2801,7 +2801,8 @@  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-redirector")) {
         return false;
     }