This could be used by VM FT solutions like Macrocheckpointing, to buffer/release packets.
Usage: -netdev tap,id=bn0 # you can use whatever backend as needed -netdev filter,id=f0,backend=bn0 -netdev filter-buffer,id=p0,filter=f0 -device e1000,netdev=f0 Will supply a public API to release the buffer. But there's no callers currently. To test this feature, it's quite simple, just use netdev_add filter-buffer,id=p0,filter=f0 to buffer packets, netdev_del p0 will release packets. Signed-off-by: Yang Hongyang <yan...@cn.fujitsu.com> --- net/Makefile.objs | 1 + net/clients.h | 3 ++ net/filter-buffer.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/net.c | 2 ++ qapi-schema.json | 20 +++++++++++++- 5 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 net/filter-buffer.c diff --git a/net/Makefile.objs b/net/Makefile.objs index 914aec0..5fa2f97 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -14,3 +14,4 @@ common-obj-$(CONFIG_SLIRP) += slirp.o common-obj-$(CONFIG_VDE) += vde.o common-obj-$(CONFIG_NETMAP) += netmap.o common-obj-y += filter.o +common-obj-y += filter-buffer.o diff --git a/net/clients.h b/net/clients.h index bcfb34b..7cc60f2 100644 --- a/net/clients.h +++ b/net/clients.h @@ -65,4 +65,7 @@ int net_init_vhost_user(const NetClientOptions *opts, const char *name, int net_init_filter(const NetClientOptions *opts, const char *name, NetClientState *peer, Error **errp); +int net_init_filter_buffer(const NetClientOptions *opts, const char *name, + NetClientState *peer, Error **errp); + #endif /* QEMU_NET_CLIENTS_H */ diff --git a/net/filter-buffer.c b/net/filter-buffer.c new file mode 100644 index 0000000..15ac903 --- /dev/null +++ b/net/filter-buffer.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang <yan...@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/net.h" +#include "net/queue.h" +#include "clients.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "net/filter.h" + +typedef struct FILTERBUFFERState { + NetClientState nc; + NetClientState *filter; + int interval; + NetQueue *inflight_queue; +} FILTERBUFFERState; + +static ssize_t filter_buffer_receive(NetClientState *nc, NetClientState *sender, + unsigned flags, const uint8_t *data, size_t size) +{ + NetQueue *queue = nc->incoming_queue; + + if (sender->info->type == NET_CLIENT_OPTIONS_KIND_NIC) { + /* we only buffer guest output packets */ + qemu_net_queue_append(queue, sender, flags, data, size, NULL); + /* Now that we have buffered the packet, return sucess */ + return size; + } + + return 0; +} + +static void filter_buffer_cleanup(NetClientState *nc) +{ + FILTERBUFFERState *s = DO_UPCAST(FILTERBUFFERState, nc, nc); + filter_del_plugin(s->filter, nc); + return; +} + + +static NetClientInfo net_filter_buffer_info = { + .type = NET_CLIENT_OPTIONS_KIND_FILTER_BUFFER, + .size = sizeof(FILTERBUFFERState), + .receive_filter = filter_buffer_receive, + .cleanup = filter_buffer_cleanup, +}; + +int net_init_filter_buffer(const NetClientOptions *opts, const char *name, + NetClientState *peer, Error **errp) +{ + NetClientState *nc; + FILTERBUFFERState *s; + const NetdevFilterBufferOptions *bufferopt; + char *filter_id = NULL; + NetClientState *filter; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_FILTER_BUFFER); + bufferopt = opts->filter_buffer; + assert(bufferopt->has_filter); + + filter_id = bufferopt->filter; + filter = qemu_find_netdev(filter_id); + if (!filter) { + error_setg(errp, "invalid filter name specified"); + return -1; + } + + nc = qemu_new_net_client(&net_filter_buffer_info, peer, "filter_buffer", name); + s = DO_UPCAST(FILTERBUFFERState, nc, nc); + s->filter = filter; + s->interval = bufferopt->has_interval ? bufferopt->interval : 0; + filter_add_plugin(filter, nc); + + return 0; +} diff --git a/net/net.c b/net/net.c index 321362f..856a0fe 100644 --- a/net/net.c +++ b/net/net.c @@ -58,6 +58,7 @@ const char *host_net_devices[] = { "socket", "dump", "filter", + "filter-buffer", #ifdef CONFIG_NET_BRIDGE "bridge", #endif @@ -907,6 +908,7 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( NetClientState *peer, Error **errp) = { [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic, [NET_CLIENT_OPTIONS_KIND_FILTER] = net_init_filter, + [NET_CLIENT_OPTIONS_KIND_FILTER_BUFFER] = net_init_filter_buffer, #ifdef CONFIG_SLIRP [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp, #endif diff --git a/qapi-schema.json b/qapi-schema.json index 9244c88..62cc54e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2491,6 +2491,23 @@ '*backend': 'str' } } ## +# @NetdevFilterBufferOptions +# +# A net filter buffer plugin +# +# @filter: the network filter this plugin attached to. +# +# @interval: #optional release packets by interval, if no interval supplied, +# will release packets when filter_buffer_release been called. +# +# Since 2.5 +## +{ 'struct': 'NetdevFilterBufferOptions', + 'data': { + '*filter': 'str', + '*interval': 'int32' } } + +## # @NetClientOptions # # A discriminated record of network device traits. @@ -2514,7 +2531,8 @@ 'hubport': 'NetdevHubPortOptions', 'netmap': 'NetdevNetmapOptions', 'vhost-user': 'NetdevVhostUserOptions', - 'filter': 'NetdevFilterOptions'} } + 'filter': 'NetdevFilterOptions', + 'filter-buffer': 'NetdevFilterBufferOptions' } } ## # @NetLegacy -- 1.9.1