@@ -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 */
@@ -92,6 +92,7 @@ struct NetClientState {
NetClientDestructor *destructor;
unsigned int queue_index;
unsigned rxfilter_notify_enabled:1;
+ QTAILQ_HEAD(, NetFilterState) filters;
};
typedef struct NICState {
@@ -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;
@@ -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;
}
new file mode 100644
@@ -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 */
@@ -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,
@@ -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.