From fdccce4596218e49ca4d0f5d4b3f0c453bd99ba0 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Wed, 7 Oct 2015 11:52:14 +0800 Subject: init/cleanup of netfilter object Add a netfilter object based on QOM. A netfilter is attached to a netdev, captures all network packets that pass through the netdev. When we delete the netdev, we also delete the netfilter object attached to it, because if the netdev is removed, the filter which attached to it is useless. Signed-off-by: Yang Hongyang Reviewed-by: Markus Armbruster Signed-off-by: Jason Wang --- net/filter.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 net/filter.c (limited to 'net/filter.c') diff --git a/net/filter.c b/net/filter.c new file mode 100644 index 0000000000..d4062591d5 --- /dev/null +++ b/net/filter.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015 FUJITSU LIMITED + * Author: Yang Hongyang + * + * 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-common.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" + +#include "net/filter.h" +#include "net/net.h" +#include "net/vhost_net.h" +#include "qom/object_interfaces.h" + +static char *netfilter_get_netdev_id(Object *obj, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + return g_strdup(nf->netdev_id); +} + +static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + + nf->netdev_id = g_strdup(str); +} + +static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED) +{ + NetFilterState *nf = NETFILTER(obj); + return nf->direction; +} + +static void netfilter_set_direction(Object *obj, int direction, Error **errp) +{ + NetFilterState *nf = NETFILTER(obj); + nf->direction = direction; +} + +static void netfilter_init(Object *obj) +{ + object_property_add_str(obj, "netdev", + netfilter_get_netdev_id, netfilter_set_netdev_id, + NULL); + object_property_add_enum(obj, "queue", "NetFilterDirection", + NetFilterDirection_lookup, + netfilter_get_direction, netfilter_set_direction, + NULL); +} + +static void netfilter_complete(UserCreatable *uc, Error **errp) +{ + NetFilterState *nf = NETFILTER(uc); + NetClientState *ncs[MAX_QUEUE_NUM]; + NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); + int queues; + Error *local_err = NULL; + + if (!nf->netdev_id) { + error_setg(errp, "Parameter 'netdev' is required"); + return; + } + + queues = qemu_find_net_clients_except(nf->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; + } else if (queues > 1) { + error_setg(errp, "multiqueue is not supported"); + return; + } + + if (get_vhost_net(ncs[0])) { + error_setg(errp, "Vhost is not supported"); + return; + } + + nf->netdev = ncs[0]; + + if (nfc->setup) { + nfc->setup(nf, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); +} + +static void netfilter_finalize(Object *obj) +{ + NetFilterState *nf = NETFILTER(obj); + NetFilterClass *nfc = NETFILTER_GET_CLASS(obj); + + if (nfc->cleanup) { + nfc->cleanup(nf); + } + + if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters)) { + QTAILQ_REMOVE(&nf->netdev->filters, nf, next); + } +} + +static void netfilter_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = netfilter_complete; +} + +static const TypeInfo netfilter_info = { + .name = TYPE_NETFILTER, + .parent = TYPE_OBJECT, + .abstract = true, + .class_size = sizeof(NetFilterClass), + .class_init = netfilter_class_init, + .instance_size = sizeof(NetFilterState), + .instance_init = netfilter_init, + .instance_finalize = netfilter_finalize, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&netfilter_info); +} + +type_init(register_types); -- cgit 1.4.1 From e64c770d1fa859bd8ee583d339b085fe345ac02b Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Wed, 7 Oct 2015 11:52:15 +0800 Subject: netfilter: hook packets before net queue send Capture packets that will be sent. Signed-off-by: Yang Hongyang Reviewed-by: Thomas Huth Signed-off-by: Jason Wang --- include/net/filter.h | 8 +++++++ net/filter.c | 17 ++++++++++++++ net/net.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) (limited to 'net/filter.c') diff --git a/include/net/filter.h b/include/net/filter.h index be27dee118..db035b6ef5 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -58,4 +58,12 @@ struct NetFilterState { QTAILQ_ENTRY(NetFilterState) next; }; +ssize_t qemu_netfilter_receive(NetFilterState *nf, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb); + #endif /* QEMU_NET_FILTER_H */ diff --git a/net/filter.c b/net/filter.c index d4062591d5..147c57f7a7 100644 --- a/net/filter.c +++ b/net/filter.c @@ -15,6 +15,23 @@ #include "net/vhost_net.h" #include "qom/object_interfaces.h" +ssize_t qemu_netfilter_receive(NetFilterState *nf, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + if (nf->direction == direction || + nf->direction == NET_FILTER_DIRECTION_ALL) { + return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov( + nf, sender, flags, iov, iovcnt, sent_cb); + } + + return 0; +} + static char *netfilter_get_netdev_id(Object *obj, Error **errp) { NetFilterState *nf = NETFILTER(obj); diff --git a/net/net.c b/net/net.c index 033f4f3387..e27643d503 100644 --- a/net/net.c +++ b/net/net.c @@ -561,6 +561,44 @@ int qemu_can_send_packet(NetClientState *sender) return 1; } +static ssize_t filter_receive_iov(NetClientState *nc, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + ssize_t ret = 0; + NetFilterState *nf = NULL; + + QTAILQ_FOREACH(nf, &nc->filters, next) { + ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, + iovcnt, sent_cb); + if (ret) { + return ret; + } + } + + return ret; +} + +static ssize_t filter_receive(NetClientState *nc, + NetFilterDirection direction, + NetClientState *sender, + unsigned flags, + const uint8_t *data, + size_t size, + NetPacketSent *sent_cb) +{ + struct iovec iov = { + .iov_base = (void *)data, + .iov_len = size + }; + + return filter_receive_iov(nc, direction, sender, flags, &iov, 1, sent_cb); +} + ssize_t qemu_deliver_packet(NetClientState *sender, unsigned flags, const uint8_t *data, @@ -632,6 +670,7 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender, NetPacketSent *sent_cb) { NetQueue *queue; + int ret; #ifdef DEBUG_NET printf("qemu_send_packet_async:\n"); @@ -642,6 +681,19 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender, return size; } + /* Let filters handle the packet first */ + ret = filter_receive(sender, NET_FILTER_DIRECTION_TX, + sender, flags, buf, size, sent_cb); + if (ret) { + return ret; + } + + ret = filter_receive(sender->peer, NET_FILTER_DIRECTION_RX, + sender, flags, buf, size, sent_cb); + if (ret) { + return ret; + } + queue = sender->peer->incoming_queue; return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb); @@ -712,11 +764,25 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender, NetPacketSent *sent_cb) { NetQueue *queue; + int ret; if (sender->link_down || !sender->peer) { return iov_size(iov, iovcnt); } + /* Let filters handle the packet first */ + ret = filter_receive_iov(sender, NET_FILTER_DIRECTION_TX, sender, + QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb); + if (ret) { + return ret; + } + + ret = filter_receive_iov(sender->peer, NET_FILTER_DIRECTION_RX, sender, + QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb); + if (ret) { + return ret; + } + queue = sender->peer->incoming_queue; return qemu_net_queue_send_iov(queue, sender, -- cgit 1.4.1 From 7ef7bc8586fb0d41742a896b532c7afa2bbb7f84 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Wed, 7 Oct 2015 11:52:18 +0800 Subject: netfilter: add an API to pass the packet to next filter add an API qemu_netfilter_pass_to_next() to pass the packet to next filter. Signed-off-by: Yang Hongyang Reviewed-by: Thomas Huth Signed-off-by: Jason Wang --- include/net/filter.h | 7 +++++++ net/filter.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) (limited to 'net/filter.c') diff --git a/include/net/filter.h b/include/net/filter.h index db035b6ef5..56399763cc 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -66,4 +66,11 @@ ssize_t qemu_netfilter_receive(NetFilterState *nf, int iovcnt, NetPacketSent *sent_cb); +/* pass the packet to the next filter */ +ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque); + #endif /* QEMU_NET_FILTER_H */ diff --git a/net/filter.c b/net/filter.c index 147c57f7a7..5d5022fc02 100644 --- a/net/filter.c +++ b/net/filter.c @@ -14,6 +14,7 @@ #include "net/net.h" #include "net/vhost_net.h" #include "qom/object_interfaces.h" +#include "qemu/iov.h" ssize_t qemu_netfilter_receive(NetFilterState *nf, NetFilterDirection direction, @@ -32,6 +33,63 @@ ssize_t qemu_netfilter_receive(NetFilterState *nf, return 0; } +ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque) +{ + int ret = 0; + int direction; + NetFilterState *nf = opaque; + NetFilterState *next = QTAILQ_NEXT(nf, next); + + if (!sender || !sender->peer) { + /* no receiver, or sender been deleted, no need to pass it further */ + goto out; + } + + if (nf->direction == NET_FILTER_DIRECTION_ALL) { + if (sender == nf->netdev) { + /* This packet is sent by netdev itself */ + direction = NET_FILTER_DIRECTION_TX; + } else { + direction = NET_FILTER_DIRECTION_RX; + } + } else { + direction = nf->direction; + } + + while (next) { + /* + * if qemu_netfilter_pass_to_next been called, means that + * the packet has been hold by filter and has already retured size + * to the sender, so sent_cb shouldn't be called later, just + * pass NULL to next. + */ + ret = qemu_netfilter_receive(next, direction, sender, flags, iov, + iovcnt, NULL); + if (ret) { + return ret; + } + next = QTAILQ_NEXT(next, next); + } + + /* + * We have gone through all filters, pass it to receiver. + * Do the valid check again incase sender or receiver been + * deleted while we go through filters. + */ + if (sender && sender->peer) { + qemu_net_queue_send_iov(sender->peer->incoming_queue, + sender, flags, iov, iovcnt, NULL); + } + +out: + /* no receiver, or sender been deleted */ + return iov_size(iov, iovcnt); +} + static char *netfilter_get_netdev_id(Object *obj, Error **errp) { NetFilterState *nf = NETFILTER(obj); -- cgit 1.4.1 From a4960f52e7f402a4b7402ace204283de7b9d4879 Mon Sep 17 00:00:00 2001 From: Yang Hongyang Date: Wed, 7 Oct 2015 11:52:19 +0800 Subject: netfilter: print filter info associate with the netdev When execute "info network", print filter info also. add a info_str member to NetFilterState, store specific filters info. Signed-off-by: Yang Hongyang Signed-off-by: Jason Wang --- include/net/filter.h | 1 + net/filter.c | 20 ++++++++++++++++++++ net/net.c | 11 +++++++++++ 3 files changed, 32 insertions(+) (limited to 'net/filter.c') diff --git a/include/net/filter.h b/include/net/filter.h index 56399763cc..2deda362a6 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -55,6 +55,7 @@ struct NetFilterState { char *netdev_id; NetClientState *netdev; NetFilterDirection direction; + char info_str[256]; QTAILQ_ENTRY(NetFilterState) next; }; diff --git a/net/filter.c b/net/filter.c index 5d5022fc02..326f2b5ac8 100644 --- a/net/filter.c +++ b/net/filter.c @@ -15,6 +15,7 @@ #include "net/vhost_net.h" #include "qom/object_interfaces.h" #include "qemu/iov.h" +#include "qapi/string-output-visitor.h" ssize_t qemu_netfilter_receive(NetFilterState *nf, NetFilterDirection direction, @@ -134,6 +135,9 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) NetFilterClass *nfc = NETFILTER_GET_CLASS(uc); int queues; Error *local_err = NULL; + char *str, *info; + ObjectProperty *prop; + StringOutputVisitor *ov; if (!nf->netdev_id) { error_setg(errp, "Parameter 'netdev' is required"); @@ -167,6 +171,22 @@ static void netfilter_complete(UserCreatable *uc, Error **errp) } } QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next); + + /* generate info str */ + QTAILQ_FOREACH(prop, &OBJECT(nf)->properties, node) { + if (!strcmp(prop->name, "type")) { + continue; + } + ov = string_output_visitor_new(false); + object_property_get(OBJECT(nf), string_output_get_visitor(ov), + prop->name, errp); + str = string_output_get_string(ov); + string_output_visitor_cleanup(ov); + info = g_strdup_printf(",%s=%s", prop->name, str); + g_strlcat(nf->info_str, info, sizeof(nf->info_str)); + g_free(str); + g_free(info); + } } static void netfilter_finalize(Object *obj) diff --git a/net/net.c b/net/net.c index c0ebb13211..39af8930b4 100644 --- a/net/net.c +++ b/net/net.c @@ -1179,10 +1179,21 @@ void qmp_netdev_del(const char *id, Error **errp) void print_net_client(Monitor *mon, NetClientState *nc) { + NetFilterState *nf; + monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name, nc->queue_index, NetClientOptionsKind_lookup[nc->info->type], nc->info_str); + if (!QTAILQ_EMPTY(&nc->filters)) { + monitor_printf(mon, "filters:\n"); + } + QTAILQ_FOREACH(nf, &nc->filters, next) { + monitor_printf(mon, " - %s: type=%s%s\n", + object_get_canonical_path_component(OBJECT(nf)), + object_get_typename(OBJECT(nf)), + nf->info_str); + } } RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name, -- cgit 1.4.1