QTAILQ_ENTRY global_list but used by filter layer, so that we can manage all filters together. QTAILQ_ENTRY next used by netdev, filter belongs to the specific netdev is in this queue. This is mostly the same with init/cleanup of netdev object.
The dummy filter is to work around with the empty union of QAPI, and will be repleced later by buffer filter. Signed-off-by: Yang Hongyang <yan...@cn.fujitsu.com> Reviewed-by: Thomas Huth <th...@redhat.com> --- v9: use flat union instead of simple union in QAPI schema v8: include vhost_net header v7: add check for vhost fix error propagate bug v6: add multiqueue support (net_filter_init1) v5: remove model from NetFilterState add a sent_cb param to receive_iov API --- include/net/filter.h | 42 ++++++++++++ include/net/net.h | 1 + include/qemu/typedefs.h | 1 + net/filter.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++ net/filters.h | 23 +++++++ net/net.c | 1 + qapi-schema.json | 60 +++++++++++++++++ 7 files changed, 301 insertions(+) create mode 100644 net/filters.h diff --git a/include/net/filter.h b/include/net/filter.h index 4242ded..ce15f15 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -9,7 +9,49 @@ #define QEMU_NET_FILTER_H #include "qemu-common.h" +#include "qemu/typedefs.h" +#include "net/queue.h" + +/* the netfilter chain */ +enum { + NET_FILTER_IN, + NET_FILTER_OUT, + NET_FILTER_ALL, +}; + +typedef void (FilterCleanup) (NetFilterState *); +/* + * Return: + * 0: finished handling the packet, we should continue + * size: filter stolen this packet, we stop pass this packet further + */ +typedef ssize_t (FilterReceiveIOV)(NetFilterState *nc, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); + +typedef struct NetFilterInfo { + NetFilterType type; + size_t size; + FilterCleanup *cleanup; + FilterReceiveIOV *receive_iov; +} NetFilterInfo; + +struct NetFilterState { + NetFilterInfo *info; + char *name; + NetClientState *netdev; + int chain; + QTAILQ_ENTRY(NetFilterState) global_list; + QTAILQ_ENTRY(NetFilterState) next; +}; int net_init_filters(void); +NetFilterState *qemu_new_net_filter(NetFilterInfo *info, + NetClientState *netdev, + const char *name, + int chain); #endif /* QEMU_NET_FILTER_H */ diff --git a/include/net/net.h b/include/net/net.h index 6a6cbef..36e5fab 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -92,6 +92,7 @@ struct NetClientState { NetClientDestructor *destructor; unsigned int queue_index; unsigned rxfilter_notify_enabled:1; + QTAILQ_HEAD(, NetFilterState) filters; }; typedef struct NICState { diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index f8a9dd6..2c0648f 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -45,6 +45,7 @@ typedef struct Monitor Monitor; typedef struct MouseTransformInfo MouseTransformInfo; typedef struct MSIMessage MSIMessage; typedef struct NetClientState NetClientState; +typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; typedef struct PcGuestInfo PcGuestInfo; typedef struct PCIBridge PCIBridge; diff --git a/net/filter.c b/net/filter.c index 4e40f08..89fb089 100644 --- a/net/filter.c +++ b/net/filter.c @@ -6,10 +6,183 @@ */ #include "qemu-common.h" +#include "qapi-visit.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "qapi-visit.h" +#include "qapi/opts-visitor.h" +#include "qapi/dealloc-visitor.h" +#include "qemu/config-file.h" + #include "net/filter.h" +#include "net/net.h" +#include "net/vhost_net.h" +#include "filters.h" + +static QTAILQ_HEAD(, NetFilterState) net_filters; + +NetFilterState *qemu_new_net_filter(NetFilterInfo *info, + NetClientState *netdev, + const char *name, + int chain) +{ + NetFilterState *nf; + + assert(info->size >= sizeof(NetFilterState)); + assert(info->receive_iov); + + nf = g_malloc0(info->size); + nf->info = info; + nf->name = g_strdup(name); + nf->netdev = netdev; + nf->chain = chain; + QTAILQ_INSERT_TAIL(&net_filters, nf, global_list); + QTAILQ_INSERT_TAIL(&netdev->filters, nf, next); + + return nf; +} + +static inline void qemu_cleanup_net_filter(NetFilterState *nf) +{ + QTAILQ_REMOVE(&nf->netdev->filters, nf, next); + QTAILQ_REMOVE(&net_filters, nf, global_list); + + if (nf->info->cleanup) { + nf->info->cleanup(nf); + } + + g_free(nf->name); + g_free(nf); +} + +static NetFilterState *qemu_find_netfilter(const char *id) +{ + NetFilterState *nf; + + QTAILQ_FOREACH(nf, &net_filters, global_list) { + if (!strcmp(nf->name, id)) { + return nf; + } + } + + return NULL; +} + +typedef int (NetFilterInit)(const NetFilter *netfilter, + const char *name, int chain, + NetClientState *netdev, Error **errp); + +static +NetFilterInit * const net_filter_init_fun[NET_FILTER_TYPE_MAX] = { + [NET_FILTER_TYPE_DUMMY] = net_init_filter_dummy, +}; + +static int net_filter_init1(const NetFilter *netfilter, Error **errp) +{ + NetClientState *ncs[MAX_QUEUE_NUM]; + const char *name = netfilter->id; + const char *netdev_id = netfilter->netdev; + const char *chain_str = NULL; + int chain, queues, i; + NetFilterState *nf; + + if (!net_filter_init_fun[netfilter->kind]) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", + "a net filter type"); + return -1; + } + + nf = qemu_find_netfilter(netfilter->id); + if (nf) { + error_setg(errp, "Filter '%s' already exists", netfilter->id); + return -1; + } + + if (netfilter->has_chain) { + chain_str = netfilter->chain; + if (!strcmp(chain_str, "in")) { + chain = NET_FILTER_IN; + } else if (!strcmp(chain_str, "out")) { + chain = NET_FILTER_OUT; + } else if (!strcmp(chain_str, "all")) { + chain = NET_FILTER_ALL; + } else { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "chain", + "netfilter chain (in/out/all)"); + return -1; + } + } else { + /* default */ + chain = NET_FILTER_ALL; + } + + queues = qemu_find_net_clients_except(netdev_id, ncs, + NET_CLIENT_OPTIONS_KIND_NIC, + MAX_QUEUE_NUM); + if (queues < 1) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev", + "a network backend id"); + return -1; + } + + if (get_vhost_net(ncs[0])) { + error_setg(errp, "vhost is not supported"); + return -1; + } + + for (i = 0; i < queues; i++) { + if (net_filter_init_fun[netfilter->kind](netfilter, name, + chain, ncs[i], errp) < 0) { + if (errp && !*errp) { + error_setg(errp, QERR_DEVICE_INIT_FAILED, + NetFilterType_lookup[netfilter->kind]); + } + return -1; + } + } + + return 0; +} + +static int net_init_filter(void *dummy, QemuOpts *opts, Error **errp) +{ + NetFilter *object = NULL; + Error *err = NULL; + int ret = -1; + OptsVisitor *ov = opts_visitor_new(opts); + + visit_type_NetFilter(opts_get_visitor(ov), &object, NULL, &err); + opts_visitor_cleanup(ov); + + if (!err) { + ret = net_filter_init1(object, &err); + } + + if (object) { + QapiDeallocVisitor *dv = qapi_dealloc_visitor_new(); + + visit_type_NetFilter(qapi_dealloc_get_visitor(dv), &object, NULL, NULL); + qapi_dealloc_visitor_cleanup(dv); + } + + if (errp) { + error_propagate(errp, err); + } else if (err) { + error_report_err(err); + } + + return ret; +} int net_init_filters(void) { + QTAILQ_INIT(&net_filters); + + if (qemu_opts_foreach(qemu_find_opts("netfilter"), + net_init_filter, NULL, NULL)) { + return -1; + } + return 0; } diff --git a/net/filters.h b/net/filters.h new file mode 100644 index 0000000..7c1f30d --- /dev/null +++ b/net/filters.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_NET_FILTERS_H +#define QEMU_NET_FILTERS_H + +#include "net/net.h" +#include "net/filter.h" + +int net_init_filter_dummy(const NetFilter *netfilter, const char *name, + int chain, NetClientState *netdev, Error **errp); + +int net_init_filter_dummy(const NetFilter *netfilter, const char *name, + int chain, NetClientState *netdev, Error **errp) +{ + return 0; +} + +#endif /* QEMU_NET_FILTERS_H */ diff --git a/net/net.c b/net/net.c index 28a5597..d9b70cd 100644 --- a/net/net.c +++ b/net/net.c @@ -287,6 +287,7 @@ static void qemu_net_client_setup(NetClientState *nc, nc->incoming_queue = qemu_new_net_queue(nc); nc->destructor = destructor; + QTAILQ_INIT(&nc->filters); } NetClientState *qemu_new_net_client(NetClientInfo *info, diff --git a/qapi-schema.json b/qapi-schema.json index 4342a08..750fc00 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2537,6 +2537,66 @@ 'opts': 'NetClientOptions' } } ## +# @NetFilterDummyOptions: +# +# An dummy filter for network backend +# +# Since: 2.5 +## +{ 'struct': 'NetFilterDummyOptions', + 'data': { } } + +## +# @NetFilterType: +# +# An enumeration of netfilter types. +# +# Since: 2.5 +## +{ 'enum': 'NetFilterType', + 'data': ['dummy'] } + +## +# @NetFilterBase +# +# Network filters which captures the packets of a network backend. +# +# @id: identifier for monitor commands +# +# @netdev: the network backend it attached to +# +# @chain: #optional accept "in","out","all", if not specified, default is "all" +# "in" means this filter will receive packets sent to the @netdev +# "out" means this filter will receive packets sent from the @netdev +# "all" means this filter will receive packets both sent to/from +# the @netdev +# +# @opts: filter type specific properties +# +# Since 2.5 +## +{ 'struct': 'NetFilterBase', + 'data': { + 'id': 'str', + 'netdev': 'str', + '*chain': 'str', + 'type': 'NetFilterType' } } + +## +# @NetFilter +# +# A discriminated record of network filters. +# +# Since 2.5 +# +## +{ 'union': 'NetFilter', + 'base': 'NetFilterBase', + 'discriminator': 'type', + 'data': { + 'dummy': 'NetFilterDummyOptions' } } + +## # @InetSocketAddress # # Captures a socket address or address range in the Internet namespace. -- 1.9.1